-
Constructive Criticism
Hey,
Well based on Gary example I've build this code:
Code:
protected void Page_Load(object sender, EventArgs e)
{
List<DataCollection> mycollection = GetData();
foreach(DataCollection dc in mycollection)
{
Trace.Warn(dc.id.ToString());
Trace.Warn(dc.loginName);
}
}
public List<DataCollection> GetData()
{
using (SqlConnection connection = new SqlConnection(GetConnectionString()))
{
using (SqlCommand command = new SqlCommand("SELECT id,login_name FROM users_details",connection ))
{
connection.Open();
return CollectData(command.ExecuteReader());
}
}
}
public List<DataCollection> CollectData(SqlDataReader reader)
{
List<DataCollection> dataCollection = new List<DataCollection>();
while (reader.Read())
{
DataCollection myClass = new DataCollection();
myClass.id = (int)reader[0];
myClass.loginName = (string)reader[1];
dataCollection.Add(myClass);
}
return dataCollection;
}
now some questions if i may:
A) is this a good practice of pulling data?
B) though i fully understand how this code works, i don't understand why using this method is better practice then returning datasets or arraylist for example?
C) should i always pull data this way and this is mean i should build a custom class for every query i have ?
thanks.
-
Re: Constructive Criticism
A) Almost. Stop passing the reader around, it's not a good idea. You haven't closed your reader. Do your object creation and reader looping in the GetData method itself.
B) It's going to be strongly typed when you use it on your page. Go ahead and try it. None of this ds.Tables[0].Rows[4]["colname"].ToString() stuff. Instead, you'd have something like dcList[4].LoginName; You know exactly what you're dealing with.
C) You should build custom classes that closely match the types of data in your database, not the queries. So if your site's about movies, create classes that represent movies and its related entities.
-
Re: Constructive Criticism
thank you the answer Mend
ok i understands and agree with A & B but i do have some questions about C:
are you saying i should build classes for my db tables and every time i need to get info from certain table i'll create an instance of that table class and use the data i need ?
also, what with Queries that involves INNER JOIN ?, I'm sure that i'm still don't see the big picture, but i think i'm getting there, i really want to understand this concept please explain a bit more :)
-
Re: Constructive Criticism
Your classes will be related to each other. An inner join is essentially a way of saying "These actors in my inner join are related to this movie." Therefore, the Actors are a property of the Movie. It can be another property in the Movie class.
public List<Actor> Actors { get; set;}
When you do your select with an inner join, you populate the usual Movie properties, you then also populate the Actors.
You eventually end up building a relationship between your classes which closely matches the database setup. In fact, if you look at your database diagram, your end goal is going to be a class diagram that closely resembles it.
-
Re: Constructive Criticism
The other thing about this is that it's going to force you to think in terms of entities and relationships. In turn, that'll force you to separate your logic into different methods instead of a big amalgamation. I'll stop there, ask questions.
-
Re: Constructive Criticism
let's say i have soccer team, and i need to to pull data of the player id, player name team name and team arena name
so it will be something like this in SQL terms
Code:
SELECT A.player_id,A.player_name,B.team_name,B.team_arena
FROM players_details A INNER JOIN teams_details B ON (A.team_id = B.team_id)
now, do i need to have 2 classes for both the player details and team details (both classes will have more details (properties) that i don't use in this query)
or i should build one class that contain both the the team and players properties ?
-
Re: Constructive Criticism
Two classes.
public class Player
{
public int PlayerID {get; set;}
public string Name {get; set;}
public Team PlayerTeam {get; set;}
}
public class Team
{
public int TeamID {get; set;}
public string TeamName {get; set;}
public string Arena {get; set;}
}
Now you run your SQL query. When doing your loop of the reader or dataset, create a new Player object, populate the ID and name. Create a new Team object, populate its name and arena, assign the Team object to the Player's PlayerTeam property.
I won't go into too much detail, but note that this is a one way relationship. You could theoretically have a property of Player called Team. You could have a property of Team called Players but that don't concern yourself with that right now. Right now, focus on bringing it close to what's in the database.
-
Re: Constructive Criticism
I need to go to sleep. work in 7 hours but i must try it first :B , i let you know once i'm done.
-
Re: Constructive Criticism
ok i think i got it.
is this what you meant?
Code:
public class PlayersDetails
{
public int id { get; set; }
public string firstName {get;set;}
public string lastName { get; set; }
public UserDetails userDetails {get;set;}
}
public class UserDetails
{
public int id { get; set; }
public string teamName { get; set; }
}
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
List<PlayersDetails> lstPlayerDetails = GetData();
foreach (PlayersDetails ff in lstPlayerDetails)
{
Trace.Warn(ff.firstName);
Trace.Warn(ff.lastName);
Trace.Warn(ff.userDetails.teamName);
}
}
protected List<PlayersDetails> GetData()
{
using (SqlConnection connection = new SqlConnection(GetConnectionString()))
{
using (SqlCommand command = new SqlCommand("SELECT A.id As Player_id,A.first_name,A.last_name,B.id AS user_id," +
" B.team_name" +
" FROM players_details A INNER JOIN users_details B " +
" ON (A.team_id=B.team_id)" +
" WHERE B.team_id = 1948", connection))
{
connection.Open();
SqlDataReader reader = command.ExecuteReader();
List<PlayersDetails> lstPlayersDetails = new List<PlayersDetails>();
while (reader.Read())
{
UserDetails userDetails = new UserDetails();
PlayersDetails playersDetails = new PlayersDetails();
userDetails.id = (int)reader["user_id"];
userDetails.teamName = (string)reader["team_name"];
playersDetails.id = (int)reader["player_id"];
playersDetails.firstName = (string)reader["first_name"];
playersDetails.lastName = (string)reader["last_name"];
playersDetails.userDetails = userDetails;
lstPlayersDetails.Add(playersDetails);
}
connection.Close();
connection.Dispose();
reader.Close();
reader.Dispose();
return lstPlayersDetails;
}
}
}
btw, should i use both .dispose() and .close or .close()/.dispose() is enough?
any other suggestions / improvements will be very appreciated.
Thanks again Mend!
-
Re: Constructive Criticism
That looks good. Have you used the List on your page yet? You can go a bit easy with the last bit - just close what you're using, don't worry about the Dispose. Also, move the return to the very end of the function and move the List<> declaration to the very beginning of the function.
Code:
reader.Close();
connection.Close();
}
}
return lstPlayersDetails;
}
-
Re: Constructive Criticism
Hey,
Nope, as far as I am aware, based on Mendhaks comments, which I am still going over in my head, you only need to call .Close(), .Dispose() is taken care of for you with the using statement.
Mendhak, question about this comment:
Quote:
A) Almost. Stop passing the reader around, it's not a good idea. You haven't closed your reader. Do your object creation and reader looping in the GetData method itself.
The reason that I did this in this way is because I have an abstract base class that does this work. I then have two derived classes, one for SQL and one for MySql. I couldn't see the point in doing the looping work in both derived classes, hence I pass an IDataReader around to the base class. That sounds legitimate to me...
Comments?
Gary
-
Re: Constructive Criticism
Passing a reader around makes for loss of ownership. So if I call some method1 and it returns a reader, then do I close the reader? Does that mean I need to close the connection too? Or if I write some method2 that takes a reader in, am I guaranteed that the fields I want will be there? Right now I'm not because I don't know what has been run against the database. The problem with the datareader is that because it's readonly forwardonly, you're holding a connection open for the duration of its usage, but the rule of thumb is to always open late, close early. You may have seen some threads in which someone decides to be clever and open a connection in a constructor somewhere, then pass it around. When you start passing these resource intensive objects around, you are opening yourself up to problems simply because the responsibility is now general rather than localized to a single method.
While having an abstract and two derived may work for your situation where I assume you're using both Sql and MySql at the same time, think about most developers - a lot of them end up coding for a "we might change data providers" scenario that never occurs. With IDataReader you do gain the advantage of being able to switch providers, but switching providers is going to have its own complications, regardless, so the gain is minimal.
-
Re: Constructive Criticism
now for performance issues, using custom classes for collecting info from the DB has any performance impact on my application? is faster/slower then using the datareader or dataset ?
another thing, how can i sort the list<CustomClass> by any of its properties ?
thanks!
-
Re: Constructive Criticism
Hey,
A Generic List has a Sort Method on it:
http://msdn.microsoft.com/en-us/library/3da4abas.aspx
Which has a default implementation that may or may not sort the list in the way that you want it to.
However, you also have the ability to pass in your own class which tells the List how you are wanting things sorted:
http://msdn.microsoft.com/en-us/library/234b841s.aspx
As for performance, have a look here for a discussion on this topic:
http://www.rosscode.com/blog/index.p...&c=1&tb=1&pb=1
My personal preference would be a custom class object, in anything other than a very simple proof of concept application.
Gary
-
Re: Constructive Criticism
Gep, nice article. Yes, that's what I'd say too, without referring to twitter or "peeps". I think you should try this in a sample app to start with at least, and you'll feel how clean and intuitive the coding is. It should take a small while to come to this way of thinking but it has its long term benefits. Try it try it try it try it
-
Re: Constructive Criticism
Try it, and grow to love it :)
-
Re: Constructive Criticism
there is no doubt i'm going to adopt this method, I already see a lot of advantage by using it and i'm sure it's only small portion of what it can offer.
Say Gary, i didn't read yet read thoroughly the sorting examples you've supplied (I still need to work once in a while :P) but is it complicated as it looks?
-
Re: Constructive Criticism
Hey,
It takes a minute to get your head round, but once you have done it a couple of times, it should become easier.
Here is a more detailed walkthrough that might be of use to you:
http://aspalliance.com/1422_Implemen...n_Generic_List
Gary
-
Re: Constructive Criticism
thanks Gary, the last link looks like it just what i needed i will go over it later @ home.
i'm not resolving this thread since there are more qustions coming up soon :)
-
Re: Constructive Criticism
No probs, post back when you have had a chance to look at it.
Gary
-
Re: Constructive Criticism
Ok what is there "next level" to this concept ?
what i mean is, i asked in previous post in this thread if i need to create Class for each one of my SQL queries, turned out that i don't.
but do i have to create a "GetData()" method for each one of my queries ? can i create a more flexible "GetData" method that can handle more then a single query?
-
Re: Constructive Criticism
Hey,
Perhaps another example will help here.
What follows is the list of methods that I return from my database. Almost all of them return some form of ArticleDetails, CommentDetails, or CategoryDetails, or List of these, but what those ArticleDetails represents changes. i.e. GetArticles, GetArticles based on a category id, etc.
Code:
// methods that work with categories
public abstract List<CategoryDetails> GetCategories();
public abstract CategoryDetails GetCategoryByID(int categoryID);
public abstract bool DeleteCategory(int categoryID);
public abstract bool UpdateCategory(CategoryDetails category);
public abstract int InsertCategory(CategoryDetails category);
// methods that work with articles
public abstract List<ArticleDetails> GetArticles();
public abstract List<ArticleDetails> GetArticles(int categoryID);
public abstract int GetArticleCount();
public abstract int GetArticleCount(int categoryID);
public abstract List<ArticleDetails> GetPublishedArticles(DateTime currentDate);
public abstract List<ArticleDetails> GetPublishedArticles(int categoryID, DateTime currentDate);
public abstract int GetPublishedArticleCount(DateTime currentDate);
public abstract int GetPublishedArticleCount(int categoryID, DateTime currentDate);
public abstract ArticleDetails GetArticleByID(int articleID);
public abstract bool DeleteArticle(int articleID);
public abstract bool UpdateArticle(ArticleDetails article);
public abstract int InsertArticle(ArticleDetails article);
public abstract bool ApproveArticle(int articleID);
public abstract bool IncrementArticleViewCount(int articleID);
public abstract bool RateArticle(int articleID, int rating);
public abstract string GetArticleBody(int articleID);
// methods that work with comments
public abstract List<CommentDetails> GetComments();
public abstract List<CommentDetails> GetComments(int articleID);
public abstract int GetCommentCount();
public abstract int GetCommentCount(int articleID);
public abstract CommentDetails GetCommentByID(int commentID);
public abstract bool DeleteComment(int commentID);
public abstract bool UpdateComment(CommentDetails article);
public abstract int InsertComment(CommentDetails article);
Here ArticleDetails, CommentDetails and CategoryDetails are all one instance of a Custom Class that contain the definition of what that thing looks like, i.e. properties, fields, etc.
Gary
-
Re: Constructive Criticism
Where do you declare those methods ?
and also, what does mean what yoo declare methods as "abstract"?
-
Re: Constructive Criticism
Hey,
This isn't something you have to worry about, what I was simply trying to show was the methods that I create in my application.
This goes back to what I was saying before, in my code, I override these methods, (since they are marked as abstract) in my derived classes for SQL Server and MySql Server. If you look at the other code I posted, you can see that I override the GetCategories() method with a specific implementation for both of the databases.
What I have shown here is basically the "contract" that each derived class has to adhere to. i.e. each derived class has to override each on the methods listed above.
Gary
-
Re: Constructive Criticism
Don't fall for the trap of trying to do too much in a single method. You're not gaining anything by doing it (unless you like spaghetti for dinner) and there's no performance difference. Create a method anytime you need to do something. GetPlayerNameById, GetPlayerTeamByPlayerId, GetPlayers, things like that.
Basically - have your method do exactly what it says on the tin. Name it after what it's supposed to do. So that when you read it later, you know exactly what it does.
-
Re: Constructive Criticism
Ok, I'm reading of the sorting methods from Gary's example above and i'm wondring, performance wise, should I use SQL sort when method or the .NET IComparable sort method?
and how do you prefer organize your classes, each one in a different page or putting them all together.
-
Re: Constructive Criticism
More questions: is there any rule i should follow when creating classes? should i try and create class for each of my tables or is it sometimes better to join tables into the same class ?
-
Re: Constructive Criticism
Create class based on your program entities. They can then be filled from table or query or something else also if need be.
In most cases an entity structure would be same as your database table, so you are getting confused between the two.
-
Re: Constructive Criticism
Quote:
Originally Posted by
motil
Ok, I'm reading of the sorting methods from Gary's example above and i'm wondring, performance wise, should I use SQL sort when method or the .NET IComparable sort method?
and how do you prefer organize your classes, each one in a different page or putting them all together.
Hey,
If you can do the sort at the database level when you are requesting the data then yes, it would make sense to do this here. This may involve passing a parameter through to the query to indicate how the data should be sorted.
However, in some cases, once you have the data, you would still want to be able to perform a sort, without having to first go back to the database.
Gary
-
Re: Constructive Criticism
Quote:
Originally Posted by
motil
More questions: is there any rule i should follow when creating classes? should i try and create class for each of my tables or is it sometimes better to join tables into the same class ?
Hey,
Yip, pretty much as Pradeep said.
To carry on the example from my own site, I have a table called Article and Category in my database, and I also have a custom class in my code to represent each of these tables. An Article has a Category, i.e. there is a foreign key relationship between the two tables, however, each one of the table are represented separately in my code.
Gary
-
Re: Constructive Criticism
Quote:
Originally Posted by
motil
Ok, I'm reading of the sorting methods from Gary's example above and i'm wondring, performance wise, should I use SQL sort when method or the .NET IComparable sort method?
and how do you prefer organize your classes, each one in a different page or putting them all together.
Depends on the situation. If you're giving the user the ability to sort by various columns, then get your data from the database without sorting, and sort it as the user asks you for it (within the limited number of rows being displayed).
-
Re: Constructive Criticism
Quote:
Originally Posted by
motil
More questions: is there any rule i should follow when creating classes? should i try and create class for each of my tables or is it sometimes better to join tables into the same class ?
Your database tables would closely, but not necessarily exactly, match your business entity relationships. The classes that you create should represent concepts that you want to work with in your application.
-
Re: Constructive Criticism
Thank you for the answers!
now, is containing methods is also one of the purposes of these classes ? for example:
i have Arena classwhich define the team arena, will it be proper if this class will contain
BuildArena and DestroyArena Methods?
-
Re: Constructive Criticism
Hey,
This is where another concept may start to poke it's head.
There is the concept of Presentation, Business Logic, and Data Access Layers, which I am sure you have heard/read about.
What we have been talking about up to now is really the Data Access Layer, the retrieval of the information from the database. To some extent, this is all that should happen in this layer, select, update, create and delete entries in your database.
Any additional logic that is performed on your information, should be performed in your Business Level logic.
Gary
-
Re: Constructive Criticism
ok, so part of the BuildArena Method is logic and part of it is INSERT / DELETE SQL Commands, where should i go from here? where and how i write my Business logic layer and how do I connect between the Business layer and the Data Access Layer?
-
Re: Constructive Criticism
Separate them.
Then from your business logic layer call the database layer methods whenever you want to do any database specific operations.
-
Re: Constructive Criticism
But it all depends on how your application is designed.
You may use the two tier architecture or three tier architecture.
In two tier approach there is no separate business logic layer. It is partially in the presentation and partially in data access layer. It is usually used for simple applications where defining three layers is too much work as compared to the project as an overall.
In three tier approach (what is being discussed above) you separate it into three parts - presentation, business logic and data access layer.
-
Re: Constructive Criticism
So, for example, i should have two classes for Players? one business logic and one for database layer? can someone please provide a very simple and basic example?
-
Re: Constructive Criticism
My project is very large, and from former experience doing things "right" is my number one target
-
Re: Constructive Criticism
Hey,
In which case, I think an additional third layer would be beneficial, as it leaves it open for future changes/enhancements etc.
Have I directed you at this sample application before?
http://www.codeplex.com/TheBeerHouse
I think I have. This is the site that I used as the basis for my own site, and where I started to learn the concepts that we have been talking about. Take a look at it, and you will see how everything gets segregated out into the appropriate layers.
I only noticed this the other day, when I was referring someone else to this project, but start with this version:
http://thebeerhouse.codeplex.com/Rel...?ReleaseId=127
Later versions include things like the MVC Framework, and more recently Entity Framework, which we haven't discussed yet.
Gary
-
Re: Constructive Criticism
We'll get to naming conventions later. Create a class PlayerDAL.cs. Create a class PlayerBL.cs
In PlayerDAL.cs
static DataSet GetPlayers(int teamID)
{
//Call database
//Fill dataset
//Return dataset
}
In PlayerBL.cs
static List<Player> GetPlayers(Team team)
{
DataSet ds = PlayerDAL.GetPlayers(team.TeamID);
//Loop through dataset
//Create player objects
//Add to list
//Return list
}
From your page,
PlayerBL.GetPlayers(tm2);
OK, so that's one way. Another preference is to have the DAL method return List<Player> and have the BL class method deal with it or do any additional processing if required and then return List<Player>.
-
Re: Constructive Criticism
Hey,
To follow on from Mendhak's example, what I have done in the past is to return a List<PlayerDetails> from the DAL. This is essentially a direct copy of the fields in the Player table. My BL then works with a List<Player> which is the correctly formatted version of the Player object, perhaps with some additional fileds based on the results of the values pulled from the database, or some other factors.
Gary
-
Re: Constructive Criticism
I do that too. My DAL returns List<PlayerDetails>. Maybe I should have mentioned that first but you get the idea I hope...
-
Re: Constructive Criticism
Thanks Gary for the example link you provided but I don't think that i can use it right now since its level is too advance for my current skill.
Mend, thank you very much for all the examples you share, they're very useful for me and help me understand things much faster.
I'm just about to improve my code and extend it to data access and business logic, i will return with the result, in the meanwhile i want to ask something else:
for preference reasons, how do you usually write your logic classes, all the class in one file (e.g. WebSiteDAL/BL) or to separate every class to a single file?
-
1 Attachment(s)
Re: Constructive Criticism
Hey,
A picture says a thousand words....
Attachment 74956
Here you can see a logical separation between the code in the BL and the DAL.
Gary
-
Re: Constructive Criticism
Quote:
Originally Posted by
motil
Thanks Gary for the example link you provided but I don't think that i can use it right now since its level is too advance for my current skill.
I know it is quite daunting, but once you get over the initial "OMG what is going on", it all starts to make sense.
You will find examples of all the things we have been talking about in that sample. I would bookmark it, and come back to it when you get some time.
Gary
-
Re: Constructive Criticism
Thanks Gary!
I will get back to the sample code once i'll more time (and experience) .
-
Re: Constructive Criticism
so, i created a new class for the BLL, but when i tried to save it in any other place beside the App_Code Directory i can't create an instance of that class, why is that ?
-
Re: Constructive Criticism
That's because that is the directory meant for executable dll files.
The other way would be to add a class library project to your solution. Then add a reference to that project in your web project so that it can be used anywhere in your web project. This way you would separate it from your web pages (presentation layer).
-
Re: Constructive Criticism
Where exactly are you trying to save it? Within a folder in the App_Code Folder? If so, Visual Studio will have altered the Namespace for you slightly, so you will need to make sure you include the full namespace in order to instantiate it.
If this isn't the problem, can you provide more details? i.e. do you get an error?
Gary
-
Re: Constructive Criticism
I created two "normal" folders inside my project "DAL" and "BLL" I created a class for the BLL and saved it there, but it seems that you can't save classes outside the APP_CODE directory cause now i can't Create an instance of the class that stored inside the the BLL directory, i think that Pradeep supplied the answer but i'm not quite sure how to do this
-
Re: Constructive Criticism
Ah, I see. Yes, you will need to create them in the App_Code Folder, but there is nothing to stop you putting them in folders within the App_Code folder.
Further segregation would be to create them in their own class libraries. That is actually what you see in the screen shot that I posted before.
Gary
-
Re: Constructive Criticism
and how do i create class library ? i searched for this option couldn't found it
-
Re: Constructive Criticism
Ah, now, are you using Visual Web Developer?
Gary
-
Re: Constructive Criticism
nope, visual studio 2008 pro
-
1 Attachment(s)
Re: Constructive Criticism
Hmm, in which case, just do this.
Right click on your solution, and select Add | New Project, then select the following:
Attachment 74961
Gary
-
Re: Constructive Criticism
Thanks guys, worked like a charm!
-
Re: Constructive Criticism
Hey,
No probs. Happy to help.
Given the length of this thread, and some of the questions, might be worth breaking out any new questions into it's own thread.
Gary
-
Re: Constructive Criticism
Well, I believe that I'll have more questions about the n-tier concept and i think that all of the questions here are related to this subject in one way or another, people that new to this concept will have great start-up point because of this thread, i think that the info you guys supplied here is better then any other article i've seen on the net (for just understanding this concept and start using it).
but if you think i should open new thread i have not problem doing so :)
-
Re: Constructive Criticism
Hey,
This is always a hard one.
Sometimes long threads like this get quite involved, and it is easy to go off course with other lines of conversation, but you are right, there is lots of good information in this thread, so maybe we should keep going with it, and try to stick as closely to the main discussion as possible.
Gary