Saving and Retrieving Web Page Control with SQL Server and VB.NET

This article describes a simple approach to storing, retrieving, and redisplaying web pages. Such might be useful if you need to store exact copies of confirmation pages (e.g., following a sales transaction) or something along those lines.

Introduction

This article describes a simple approach to storing, retrieving, and redisplaying web pages. Such might be useful if you need to store exact copies of confirmation pages (e.g., following a sales transaction) or something along those lines.

The example provided is in the form of a simple application that simulates a sales oriented web site; the example uses an SQL Server database and LINQ to SQL. Pages are stored and retrieved from the database for redisplay in a web browser.

StrorWebpage1-in-vb.net.gif

Figure 1: Demo Website

Getting Started:

In order to get started, unzip the included project and save it to your hard drive. Open the web application and examine the contents in the solution explorer.

StrorWebpage2-in-vb.net.gif

Figure 2: Solution Explorer

The solution contains a single web application project called "WebPageStore". This web application contains three web pages (default, order, and review) along with a single master page; all code behind is in VB. The default web page is used to display a simulation or an ordering system, the order web page is used to display a simulation of an order confirmation, and the review page is used to display retrieved web pages.

The app code folder contains a DBML file supporting the LINQ to SQL interface to the database.

The bulk of the code is there just to provide a simple framework for creating something to save and restore for redisplay. The relevant parts of the code used to actually store and recall the order page will be pointed out in the following sections.


Code: Default.aspx

The default page is used to create an order so that we have something to save to the database. It is a nonsense web page that badly simulates an online ordering system for a fake company called, "Dangerous Pets". The purpose of the page is to allow the user to review product descriptions and enter a count for the number of items that the user wishes to order at the given price.

This information is used to generate an order confirmation and it is the order confirmation that is persisted to the database.

There is nothing particularly interesting about the page's markup; it contains little more than a table used to display a fictitious catalog of scary pets available for sale. You may open the page from the attached demonstration project if you'd like to review its content; it won't include it here as it is not important to the issue of this article.

Code: Default.vb

The code behind for the default page it equally uninspiring; it contains only a single button click event handler used to complete a phony order by pushing any order values captured from the default page into session variables and then redirecting the user to the order page.

Protected Sub btnSubmitOrder_Click(ByVal sender As Object, _
ByVal e As System.EventArgs) Handles btnSubmitOrder.Click
' alligators
If (Not String.IsNullOrEmpty(txtAlligators.Text)) Then
Session("Alligators") = txtAlligators.Text
End If
' Banana Slugs
If (Not String.IsNullOrEmpty(txtBananaSlugs.Text)) Then
Session("BananaSlugs") = txtBananaSlugs.Text
End If
' Black Widows
If (Not String.IsNullOrEmpty(txtBlackWidows.Text)) Then
Session("BlackWidows") = txtBlackWidows.Text
End If
' Fugu
If (Not String.IsNullOrEmpty(txtFugu.Text)) Then
Session("Fugu") = txtFugu.Text
End If
' Rattlesnakes
If (Not String.IsNullOrEmpty(txtRattlesnakes.Text)) Then
Session("Rattlesnakes") = txtRattlesnakes.Text
End If
' Scorpions
If (Not String.IsNullOrEmpty(txtScorpions.Text)) Then
Session("Scorpions") = txtScorpions.Text
End If
' Venus Fly Traps
If (Not String.IsNullOrEmpty(txtVenusFlyTraps.Text)) Then
Session("VenusFlyTraps") = txtVenusFlyTraps.Text
End If
Response.Redirect("Order.aspx")
End Sub
End
Class


Code: Order.aspx

The order page is used to display a phony order confirmation page. There is nothing particularly interesting about it; it merely displays the counts for each item ordered along with a price total. Again, you can review the content of the page from the attached project.

Code: Order.vb

The form class contains the code used to save the page to an SQL Server database. This class begins with the declaration of some variables used to display the order information on the order page:

Partial Class Order
Inherits System.Web.UI.Page

' Variable Declarations

' counts for products
Protected alligatorCount As Integer
Protected
bananaSlugCount As Integer
Protected
blackWidowsCount As Integer
Protected
fuguCount As Integer
Protected
rattlesnakeCount As Integer
Protected
scorpionCount As Integer
Protected
venusFlyTrapsCount As Integer

' subtotals for each quantity of
' ordered items
Protected alligatorAmount As Double
Protected
bananaSlugAmount As Double
Protected
blackWidowsAmount As Double
Protected
fuguAmount As Double
Protected
rattlesnakeAmount As Double
Protected
scorpionAmount As Double
Protected
venusFlyTrapsAmount As Double

' sums for both quantity and amount
Protected totalCount As Integer
Protected
totalAmount As Double


Following the variable declarations, the page load event handler first populates all of the variables with the counts, subtotals for each item, and final total for the order.

''' <summary>
''' On page load, sum up all the quantities, subtotals
''' and totals for display and place each
''' calculation into the session. If the user
''' buys a product, save the page to an sql server
''' database.
'''
</summary>
''' <param name="sender"></param>
''' <param name="e"></param>
''' <remarks></remarks>
Protected Sub Page_Load(ByVal sender As Object, _
ByVal e As System.EventArgs) Handles Me.Load

' Alligator count and total
Try
alligatorCount = Convert.ToInt32(Session("Alligators"))
Catch ex As Exception
alligatorCount = 0
End Try
' Total
alligatorAmount = alligatorCount * 22.15

' Banana Slug count and total
Try
bananaSlugCount = Convert.ToInt32(Session("BananaSlugs"))
Catch ex As Exception
bananaSlugCount = 0
End Try
' Total
bananaSlugAmount = bananaSlugCount * 1.25

' Black Widows
Try
blackWidowsCount = Convert.ToInt32(Session("BlackWidows"))
Catch ex As Exception
blackWidowsCount = 0
End Try
' Total
blackWidowsAmount = blackWidowsCount * 2.99

' Fugu
Try
fuguCount = Convert.ToInt32(Session("Fugu"))
Catch ex As Exception
fuguCount = 0
End Try
' Total
fuguAmount = fuguCount * 9.0

' Rattlesnake
Try
rattlesnakeCount = Convert.ToInt32(Session("Rattlesnakes"))
Catch ex As Exception
rattlesnakeCount = 0
End Try
' Total
rattlesnakeAmount = rattlesnakeCount * 12.77

' Scorpions
Try
scorpionCount = Convert.ToInt32(Session("Scorpions"))
Catch ex As Exception
scorpionCount = 0
End Try
' Total
scorpionAmount = scorpionCount * 1.55

' Venus Fly Traps
Try
venusFlyTrapsCount = Convert.ToInt32(Session("VenusFlyTraps"))
Catch ex As Exception
venusFlyTrapsCount = 0
End Try
' Total
venusFlyTrapsAmount = venusFlyTrapsCount * 3.25

' the total count of all products ordered
totalCount = alligatorCount + bananaSlugCount _
+ blackWidowsCount + fuguCount + rattlesnakeCount + _
scorpionCount + venusFlyTrapsCount

totalAmount = alligatorAmount + bananaSlugAmount + _
blackWidowsAmount + fuguAmount + rattlesnakeAmount + _
scorpionAmount + venusFlyTrapsAmount


After setting the variables, the next part of the load event handler checks to see if anything was ordered and then building a record; in this case the record only requires an ID (as a GUID) and the content containing the page's HTML.

The Page.RenderControl method is used to populate the html text writer with the page content; this is then converted to a byte array which is subsequently stored in the database along with the order ID using LINQ to SQL.

' if the user orders at least one product, save
' the order to the database.
If totalCount > 1 Then

' save html to database
Try

' create ID for the record
Dim savedConfirmId As Guid
savedConfirmId = Guid.NewGuid

' get the html converted to a byte array
Dim stringWriter As New System.IO.StringWriter()
Dim htmlWriter As New
System.Web.UI.HtmlTextWriter(stringWriter)

Page.RenderControl(htmlWriter)
htmlWriter.Flush()

Dim str As String = stringWriter.ToString()
Dim bArr As Byte() = System.Text.Encoding.UTF8.GetBytes(str)

' Create a new saved order object (LINQ to SQL)
' and populate the ID and content fields
Dim so As New saved_order
so.ID = Guid.NewGuid
so.Content = bArr
' Get an instance of the data context and
' try to submit the new order for storage
Dim context As New WsdalDataContext
context.saved_orders.InsertOnSubmit(so)
context.SubmitChanges()

Catch ex As Exception

' if any errors occur, display them
Response.Write(ex.ToString())

End Try
End
If
End Sub

Code: Review.aspx

The form class contains a single literal control used to display an order retrieved from the database. You can review the page content in the attached project if so desired.

Code: Review.vb

This form class is used to display a stored order confirmation page; the execution is straightforward in the example; the order ID is passed in the query string and used in a LINQ to SQL call to recover the page content associated with the order ID from the database.

The content, stored as a byte array, is converted to a string which is then passed to the literal control used to display the stored page. The entire class is as follows:

Imports System.Data.Linq

Partial Class Review
Inherits System.Web.UI.Page

''' <summary>
''' Get the query string (containing the order ID), retrieve the
''' matching record, and display the content in a literal control
''' contained on the review page
'''
</summary>
''' <param name="sender"></param>
''' <param name="e"></param>
''' <remarks></remarks>
Protected Sub Page_Load(ByVal sender As Object, _
ByVal e As System.EventArgs) Handles Me.Load

' get the order id from the query string
Dim strId As String = Request.QueryString("orderId")
Dim context As New WsdalDataContext
' Retrieve the page content from the database (binary)
Dim pageContent = (From a In context.GetTable(Of saved_order)() _
Where a.ID.ToString() = strId _
Select a.Content).First()

' display the order confirmation after converting
' the binary back to a string containing the original HTML
Dim htmlPageContent As String = _
ConvertBinaryToString(pageContent.ToArray())

' display the retrieved page in a literal cotnrol
litPage.Text = htmlPageContent
End Sub

''' <summary>
''' Convert the byte array into a string; used
''' here to put the html back into a usable format
'''
</summary>
''' <param name="input"></param>
''' <returns></returns>
''' <remarks></remarks>
Public Function ConvertBinaryToString(ByVal input() As Byte) As String

Dim enc As New System.Text.ASCIIEncoding()
Dim str As String = enc.GetString(input)
Return str

End Function

End Class

Code: MasterPage.master

The master page contains a simple banner along with a drop down list used to display existing order IDs. A button adjacent to the drop down list is used to redirect the page to the review page and to format the query string containing the order ID used by the review page to recover and display the stored order page.

You may review the markup from the attached project.

Code: MasterPage.master.vb

The code behind for the master page contains the code used to generate a list of orders which are used to populate a drop down list. That list is used to define which order to recover and redisplay.

An image button's click event handler is used to redirect the user to the review page; the currently selected order from the drop down list is used to format a query string which is in turn used in the review page to determine which order to recover and display. The code is annotated and easy to follow:

Partial Class MasterPage
Inherits System.Web.UI.MasterPage

''' <summary>
''' Update the list of stored orders each time
''' the page is loaded
'''
</summary>
''' <param name="sender"></param>
''' <param name="e"></param>
''' <remarks></remarks>
Protected Sub Page_Load(ByVal sender As Object, _
ByVal e As System.EventArgs) Handles Me.Load

If Not Page.IsPostBack Then
UpdateList()
End If

End Sub

''' <summary>
''' Redirect to the order review page passing the
''' order ID in the query string
'''
</summary>
''' <param name="sender"></param>
''' <param name="e"></param>
''' <remarks></remarks>
Protected Sub imgRetrieveOrder_Click(ByVal sender As Object, _
ByVal e As System.Web.UI.ImageClickEventArgs) Handles
imgRetrieveOrder.Click()
Try
Response.Redirect("Review.aspx?orderId=" & _
ddlOrderList.SelectedItem.Text)
Catch ex As Exception
Response.Write(ex.Message)
End Try
End
Sub

''' <summary>
''' Use LINQ to SQL Data Context to refresh
''' the list of stored orders whenever this
''' method is called
'''
</summary>
''' <remarks></remarks>
Protected Sub UpdateList()

Dim context As New WsdalDataContext()
Dim orders = (From a In context.GetTable(Of saved_order)() _
Select a.ID).ToList()

ddlOrderList.Items.Clear()
ddlOrderList.DataSource = orders
ddlOrderList.DataMember = ID
ddlOrderList.DataBind()

End Sub

End Class

Code: Wsdal.dbml

The DBML maintains a connection to the WebStore database; the visual designer contains only the saved orders table. You can review this in the project if you'd like but that is all there is to it.

Database

The database used for this demonstration contains only a single table containing only two fields, ID and content. If you decide to run the demonstration project, create a database called "WebStore " and a single table (saved_orders) with the following fields and update the project connection string to point to that database.

StrorWebpage3-in-vb.net.gif

Figure 3. Saved Orders Table

Summary

The article addresses a simple process for capturing a displayed webpage and persisting that page to a database. Further, the article addresses the recovery and redisplay of web pages stored in this manner. This approach might be useful to anyone that wants to store an exact copy of a page as it was displayed to a user, for example, an order confirmation.