
ASP.NET 2.0 Instant Results
.pdf
6
Wrox Blog
Undoubtedly, blogging — a contraction of web and logging — is the most popular Internet application of the past few years. Relatively new to the Internet world, blogging has now reached millions of web users, turning them into mini-publishers of news, diaries, and biographies.
Blogging applications come in all sorts and sizes. There are subscription-based blog sites from commercial companies, where you need to sign up for an account. Once you have an account, you can sign in to the company’s web site to manage your blog. People who want to read your blog should come to that same site. Other companies provide blogging services that allow you to manage your blog entries on their site, and make them available publicly (for example, on your own site) through XML and Web Services. Yet other organizations have developed ready-made blogging applications that you can install on your own server. Some of them come as free or open source packages, of which probably the most well known is Community Server (www.communityserver.org), which is also the driving force behind the forums on Microsoft’s community site, www.asp.net.
Despite the many advantages these ready-made applications have, they all share one disadvantage: They are often hard to incorporate into your web site, especially when you don’t control your own server but use a shared hosting service instead.
The Wrox Blog application presented in this chapter is really easy to integrate in an existing web application. This is accomplished by a few design decisions.
First of all, the Blog application does not contain normal ASPX WebForms; it consists solely of user controls. This makes it easy to embed the Blog application in existing pages, simply by dragging a few controls onto your page.
Secondly, the Blog application is designed to work with both a Microsoft Access database and with SQL Server 2005. This can be very useful if you have a host that hasn’t upgraded to SQL Server 2005 yet, or if you have to pay for the additional SQL Server services. Switching the application over from SQL Server to Access or vice versa is as simple as changing two settings in the Web.config file. This means you could even switch without taking the site offline.

Chapter 6
The first section in this chapter shows you how to use the Blog application. You see how the user controls are incorporated in standard ASPX pages, enabling you to view and manage your blog.
The section “Design of the Wrox Blog” describes the design principles behind the Wrox Blog application. You see the classes involved, and how they interact together. You also see the design of the SQL Server and Microsoft Access databases.
The section that follows, “Code and Code Explanation,” gives you a good look at the classes, pages, and the code-behind files that make up the Wrox Blog application.
In “Setting up the Wrox Blog” you get a thorough explanation of setting up the Wrox Blog. You see how to run the supplied installer to create a full web site that uses the Blog application. You also learn how to incorporate the Blog application in an existing web site. But, first things first: learning how to use the Wrox Blog.
Using the Blog
Using the Wrox Blog web site is very easy. If you know how to use a word processor, you should have no problems creating content for your blog site. When you have installed the Wrox Blog (see the section “Setting up the Wrox Blog” near the end of this chapter for details) you can browse to the site by going to http://localhost/Blog. The start page shown in Figure 6-1 appears.
Figure 6-1
168

Wrox Blog
This page contains two user controls; one for the left-hand bar with the calendar and the category list and one for the items in the list on the right, referred to as blog entries or blog posts. The left-hand bar displays a calendar and a list with the categories available in the system. The number between the parentheses indicates how many blog posts have been published in that category. Both the calendar and the category list are used to filter the list with blog entries you see in the right-hand pane. When the page first loads, that pane displays the latest 15 blog entries in the system (or fewer when there aren’t that many entries in the database). When you click a date in the calendar, the list on the right updates itself to display the blog entries for that date. You can also click the greater than symbol (>) on the left of the calendar to see entries posted during an entire week. If you click one of the categories, you’ll see all the entries posted in that category.
To manage blog entries on the web site, you need to log in first. Once you’re authenticated, you’ll see a Create New Entry button appear at the bottom of the page, as shown in Figure 6-2.
Figure 6-2
If you click that button, the page refreshes and shows a screen (see Figure 6-3) where you can enter a new blog entry.
Here you can enter a title, the post’s body text, and the category in which you want to publish the item. The calendar’s selected date defaults to today’s date, but to allow you to predate your blog entries you can select a different date from the calendar.
Once you click the Save button, the entry appears on the web site and can be viewed by selecting the appropriate category or date on which the item was published.
If you’re logged in, you also see an Edit This Entry link after each blog entry’s title. Clicking this link brings up a similar edit screen with all the details already filled in, so you can easily change the entry.
Now that you know how to use the Wrox Blog application, it’s time to take a look at its design. In the next section you see how the Wrox Blog application is designed, what classes are involved, and how the code is able to operate with different kinds of databases.
169

Chapter 6
Figure 6-3
Design of the Wrox Blog
The Wrox Blog is designed as a three-tier application, meaning the application consists of a presentation layer, a business layer, and a data access layer. The presentation layer consists of two ASP.NET user controls that are discussed later in the chapter. Both the business and the data access layers contain custom classes defined in the files in the special App_Code folder in the root of the web site. You find the files related to the business layer in the subfolder called BusinessLogic, and the data access layer is placed in the DataAccess folder. This distinction isn’t really necessary, because the ASP.NET run time compiles each file it finds in the App_Code folder or any of its subfolders automatically. However, placing the files in separate folders makes it easier to see to what layer each file and class belongs. The files in the folder are named after the classes they contain, so you’ll find the class BlogManager in the file BlogManager.vb, and so on.
The Business Layer
The business layer of the Wrox Blog is built around two important entities: the BlogEntry and the BlogManager classes. The BlogEntry class represents a blog entry that is stored in the database and can be viewed on the web site, and the BlogManager class is responsible for getting the blog entries in and out of the database. In addition to these two important classes, you’ll also find a UserManager class in
170

Wrox Blog
the BusinessLayer folder. This class is used to allow users to log in and retrieve information about the roles they are assigned to when you’re using an Access database. ASP.NET 2.0 provides a very convenient platform that handles user authentication and role management for you. However, this framework works only with SQL Server and not with a Microsoft Access database. To still allow you to log in to the site when you’re using an Access database, the UserManager class provides the necessary methods.
To see what these classes can do, each of them is discussed in the following sections.
The BlogEntry Class
The BlogEntry class is used to represent a blog post that gets stored in the database and that can be viewed on the web site. All the interaction with a BlogEntry instance is done by the BlogManager. Therefore, the BlogEntry class, depicted in Figure 6-4, has only public properties and no methods (other than its two constructors).
Figure 6-4
The following table lists all of the public properties of the BlogEntry class:
Property |
Data Type |
Description |
|
|
|
Body |
String |
This property holds the text for the blog entry. |
CategoryId |
Integer |
This indicates to which category the blog entry belongs. |
DatePublished |
DateTime |
This property holds the date and time the blog entry was |
|
|
published. |
Id |
Integer |
This is the unique ID of the blog entry and is assigned by |
|
|
the database automatically whenever a new item is created. |
Title |
String |
This is the title of the blog entry as it appears on the |
|
|
BlogEntries user control. |
|
|
|
In addition to these five properties, the BlogEntry class has two constructors. The first, a parameterless default constructor, is used to create an entirely new BlogEntry instance. The second, an overloaded
171

Chapter 6
version that accepts a BlogEntry instance’s ID as an Integer, is used when an existing BlogEntry is re-created when it’s being edited. The ID passed to the constructor is stored in the private field_Id and is made available through the public and read-only property Id. You see both constructors at work later when the code is discussed in more detail.
Because the BlogEntry class is used only to hold data and cannot perform any operations, another class is needed that can work with instances of the BlogEntry. That class is the BlogManager.
The BlogManager Class
Quite the opposite of the BlogEntry class, the BlogManager class (see Figure 6-5) has only shared methods and no properties. It also has one private constructor to prevent calling code from creating instances of the BlogManager class.
Figure 6-5
As you can see by the method names in Figure 6-5, the BlogManager class is not only responsible for working with blog entries, but is also capable of retrieving a list of categories. In larger applications it would be a wise design decision to introduce separate Category and CategoryManager classes, but in a relatively small application like the Wrox Blog it’s perfectly acceptable to designate one class for multiple tasks.
These methods need some explanation, so the following table describes all of them in more detail:
Method |
|
Return Type |
Description |
|
|
|
|
Public Shared |
Function |
DataSet |
This method returns the latest 15 blog |
GetBlogEntries () As |
|
entries from the database by calling into |
|
DataSet |
|
|
the BlogManagerDB class. |
Public Shared |
Function |
DataSet |
Returns all blog entries in the specified |
GetBlogEntries (ByVal |
|
category from the database by calling |
|
categoryId As |
Integer) |
|
into the BlogManagerDB class. |
As DataSet |
|
|
|
Public Shared |
Function |
DataSet |
Returns all blog entries in the specified |
GetBlogEntries (ByVal |
|
period from the database by calling into |
|
startDate As DateTime, |
|
the BlogManagerDB class. If startDate |
|
ByVal endDate |
As DateTime) |
|
and endDate are the same, blog entries |
As DataSet |
|
|
are returned for a single day. |
|
|
|
|
172

|
|
|
Wrox Blog |
|
|
|
|
|
Method |
Return Type |
Description |
|
|
|
|
|
Public Shared Function |
BlogEntry |
This method retrieves a single BlogEntry |
|
GetBlogEntry (ByVal |
|
instance from the database based on the |
|
blogEntryId As Integer) |
|
blogEntryId passed to this method. It |
|
As BlogEntry |
|
does this by calling GetBlogEntry in |
|
|
|
the BlogManagerDB class. Because this |
|
|
|
method is only used when editing blog |
|
|
|
entries, the code checks if the current user |
|
|
|
is a member of the Administrator group |
|
|
|
and throws an exception if this isn’t the |
|
|
|
case. |
|
Public Shared Function |
DataSet |
Returns the available categories as a |
|
GetCategories () As |
|
DataSet. |
|
DataSet |
|
|
|
Public Shared Sub |
n/a |
This method saves a blog entry in the |
|
SaveBlogEntry (ByVal |
|
database. This can be a completely new |
|
myBlogEntry As BlogEntry) |
|
or an updated blog entry. Just as with |
|
|
|
GetBlogEntry, this method checks the |
|
|
|
access rights of the current user. |
|
|
|
|
In Figure 6-5 you see the method GetBlogEntries with (+ 2 overloads) behind its name. In the table describing the methods, you see GetBlogEntries listed three times. Although the name of these three methods is the same, their argument lists differ. There is a version without arguments, one that accepts the ID of the category, and one that accepts a start and end date. To avoid cluttering up the class diagram, these methods have been grouped together under one method name in Figure 6-5. To help you see the method has overloads, (+ 2 overloads) is put behind the method name.
The class diagram in Figure 6-5 also shows a New method with a little lock icon in front of it. This is the constructor for the BlogManager class. Because this class exposes only shared methods (that operate on the class itself, rather than on an instance of the class) the constructor has been hidden by marking it as private. This makes it impossible for calling code to create new instances of the BlogManager class. All classes in the App_Code folder for the Wrox Blog except the BlogEntry class follow this pattern and thus have a private constructor.
The final class in the BusinessLayer folder is the UserManager class, which is discussed next.
The UserManager Class
The ASP.NET 2.0 Framework provides very powerful yet easy-to-use features to manage authentication and role membership. These features are referred to as the Membership and Role providers. By simply activating these features in your application’s configuration, the application is able to allow users to log in and grant them different rights depending on the roles they are assigned to. These providers have one great shortcoming: They work only with SQL Server and not with another database such as Microsoft Access. The provider model allows developers to override the behavior of the default providers, so it
is possible to write your own providers that work with an Access database instead of with SQL Server. Because of the large amount of functionality and methods these providers offer, writing your own
173

Chapter 6
provider can easily be the subject of an entire chapter or book. Because the Wrox Blog doesn’t need all this functionality, it contains a simple alternative in the form of the UserManager class. This class has a single method called GetUserRoles (see Figure 6-6) that retrieves the roles for a user.
Figure 6-6
The GetUserRoles method accepts the username and a hash of the user’s password and returns a list with the roles the user has been assigned to (that is, if the user was found in the database, of course). These roles are then used by the application to determine the access rights for the user. This method is used in the Login page that is discussed later.
For the BlogManager and the UserManager classes in the business layer, you’ll find a database counterpart that ends with DB inside the DataAccess folder. These classes carry out database interaction and are discussed next.
The Data Access Layer
One of the main design goals for the Wrox Blog was database independence. Because it’s likely you use this application with a remote host, you can’t know in advance whether that hosts supports SQL Server 2005 or just Microsoft Access. So, the code should work with SQL Server and with a Microsoft Access database without any modification. When you look at the three classes present in the data access layer, you won’t be able to see that these can work with multiple databases at the same time. Instead, these classes expose a single interface with methods that can work with different kinds of databases. This is made possible by a concept called factories, something you see more about when the code is discussed later. To understand how this all works, and why this is so great, you need to look a bit at the history of ASP and ADO.
When the .NET Framework version 1.0 was released, one area that caused a lot of confusion among developers was the way databases were accessed. In classic ASP — or to be more exact, with classic ADO — you had a single object model that could work with a wide variety of databases. Simply by passing a proper connection string to a Connection object you could talk to SQL Server, Access, Oracle, and other databases. Recordsets retrieved through that connection always worked the same, and exposed the same set of methods. However, with .NET, things changed drastically. Instead of a generic Connection or Recordset object, developers were faced with objects bound to specific providers. For example, for the SQL Server provider, you have a SqlConnection and a SqlDataReader; for the OleDb provider there is an OleDbConnection and an OleDbDataReader; and so on. The only exception is the DataSet that, instead of being tied to a specific data provider, is hosted in the general System.Data namespace. Though these strongly typed objects brought great performance and a rich feature set targeted at the specific provider, developers wanting to target both SQL Server and Oracle or any other database at the same time were faced with a huge challenge. To work around this problem, a few methods are available.
174

Wrox Blog
First, there is the abstract base class model. In this model, a designer creates an abstract base class (a class that must be inherited and cannot be instantiated directly) or an interface that supplies the signature of each of the necessary methods, like GetBlogEntry and GetCategoriesList. Then for each required database provider a child class is created that inherits from this base class or implements the appropriate interface. This concrete child class then implements each of the methods defined in the contract of the base class or interface. At run time, the proper child class is instantiated and the appropriate methods are called. This solution results in good performance because each of the child classes uses the most appropriate data providers, so the SQL Server implementation of the child class can benefit from the optimizations found in the SQL Server provider, for example. The downside of this solution is the amount of code required. For each new database provider, an entirely new child class needs to be created and maintained.
Another solution to write database-independent code is to write against the generic interfaces of each of the important data access objects. Each of the main ADO.NET objects, like a Connection, a DataReader, and so on, implements a specific interface. The SqlConnection implements IdbConnection, an OleDbDataReader implements IdataReader, and so on. With this solution, you have to create a method that returns the proper object type, based on the provider you want to use. This method could look similar to this:
Public Function GetConnection() As IDbConnection
Select Case GetProviderFromConfiguration()
Case “System.Data.SqlClient”
Return New SqlConnection()
Case “System.Data.OleDb”
Return New OleDbConnection()
End Select
End Function
This method looks up the requested provider from the application’s configuration file, for example, and returns the appropriate connection type. The biggest downside of this method is that you use the generic interface shared by all providers. This means you can, by default, only use the common denominator shared by all providers. It also means that you should modify this code whenever a new provider is added to the application.
Along come .NET 2.0 and ADO.NET 2.0 with a factories pattern that solves many of these problems. In a factory pattern, a class is responsible for creating instances of other classes. In the Wrox Blog, the DbProviderFactories class from the System.Data.Common namespace is used. This class is able to create instances of other classes that are used to interact with databases. In terms of design, the
ADO.NET factories pattern looks a lot like the generic interface solution you just saw. However, implementing it is now a lot more straightforward. You see the code to actually implement this later in the section “Writing Provider-Independent Code.”
Even though .NET 2.0 fixes many of the problems related to object instantiation, some impacting differences between each data provider still exist that make it difficult to write data provider-independent code. These differences include the use of built-in functions, the capabilities of stored procedures, and the way parameters are passed to stored procedures. Not all of these problems can be fixed completely, but with some careful planning and some smart helper code it is possible to work around most of these limitations. Later in this chapter, when the inner workings of the code are discussed in the data access layer, you see the code responsible for these workarounds.
175

Chapter 6
Now that you have some background on the design goals and decisions made for the data access layer of the Wrox Blog, take a look at the actual classes defined in this layer. Because the BlogEntry class in the business layer does not have its own behavior, you’ll see no BlogEntryDB class in the data access layer. Instead, all interaction with the database to get information about blog entries and to save them is carried out by the BlogManagerDB class.
The BlogManagerDB Class
The BlogManagerDB class has the exact same methods as the BlogManager class. However, the BlogManagerDB class is responsible for actually getting the requested data from and into the database. The BlogManagerDB class does not only work with blog items or lists of entries; it’s also responsible for getting a list with the available blog categories from the database.
Figure 6-7 lists the four methods and the private constructor defined in this class.
Figure 6-7
Each of these methods and their overloads, except the constructor, are discussed in the following table:
Method |
Return Type |
Description |
|
|
|
Public Shared Function |
DataSet |
This method returns the latest 15 blog entries |
GetBlogEntries () As |
|
from the database, again by calling a stored |
DataSet |
|
procedure or query. |
Public Shared Function |
DataSet |
Returns all blog entries in the specified |
GetBlogEntries (ByVal |
|
category from the database. |
categoryId As Integer) |
|
|
As DataSet |
|
|
Public Shared Function |
DataSet |
Returns all blog entries in the specified period |
GetBlogEntries (ByVal |
|
from the database. If startDate and endDate |
startDate As DateTime, |
|
are the same, entries are returned for a single |
ByVal endDate As |
|
day. |
DateTime) As DataSet |
|
|
Public Shared Function |
BlogEntry |
This method retrieves a single BlogEntry |
GetBlogEntry (ByVal |
|
instance from the database based on the |
blogEntryId As |
|
blogEntryId passed to this method. It does |
Integer) As BlogEntry |
|
this by calling a stored procedure (or query) in |
|
|
the database. The procedures used in the data |
|
|
access layer are discussed later. |
|
|
|
176