ASP.NET 4 in VB 2010 : Reading and Writing with Streams (part 2) - Shortcuts for Reading and Writing Files, A Simple Guest Book

1/28/2013 6:20:59 PM

3. Shortcuts for Reading and Writing Files

.NET includes functionality for turbo-charging your file writing and reading. This functionality comes from several shared methods in the File class that let you read or write an entire file in a single line of code.

For example, here's a quick code snippet that writes a three-line file and then retrieves it into a single string:

Dim lines() As String = {"This is the first line of the file.", _
  "This is the second line of the file.", _
  "This is the third line of the file."}

' Write the file in one shot.
File.WriteAllLines("c:\testfile.txt", lines)

' Read the file in one shot (into a variable named content).
Dim content As String = File.ReadAllText("c:\testfile.txt")

Table 1 describes the full set of quick file access methods. All of these are shared methods.

Table 1. File Methods for Quick Input/Output
ReadAllText()Reads the entire contents of a file and returns it as a single string.
ReadAllLines()Reads the entire contents of a file and returns it as an array of strings, one for each line.
ReadAllBytes()Reads the entire file and returns its contents as an array of bytes.
WriteAllText()Creates a file, writes a supplied string to the file, and closes it. If the file already exists, it is overwritten.
WriteAllLines()Creates a file, writes a supplied array of strings to the file (separating each line with a hard return), and closes the file. If the file already exists, it is overwritten.
WriteAllBytes()Creates a file, writes a supplied byte array to the file, and closes it. If the file already exists, it is overwritten.

The quick file access methods are certainly convenient for creating small files. They also ensure a file is kept only for as short a time as possible, which is always the best approach to minimize concurrency problems. But are they really practical? It all depends on the size of the file. If you have a large file (say, one that's several megabytes), reading the entire content into memory at once is a terrible idea. It's much better to read one piece of data at a time and process the information bit by bit. Even if you're dealing with medium-sized files (say, several hundreds of kilobytes), you might want to steer clear of the quick file access methods. That's because in a popular website you might have multiple requests dealing with files at the same time, and the combined overhead of keeping every user's file data in memory might reduce the performance of your application.

4. A Simple Guest Book

The next example demonstrates the file access techniques described in the previous sections to create a simple guest book. The page actually has two parts. If there are no current guest entries, the client will see only the controls for adding a new entry, as shown in Figure 2.

Figure 2. The initial guest book page

When the user clicks Submit, a file will be created for the new guest book entry. As long as at least one guest book entry exists, a GridView control will appear at the top of the page, as shown in Figure 3.

Figure 3. The full guest book page

Technically speaking, the GridView is bound to a collection that contains instances of the BookEntry class. The BookEntry class definition is included in the code-behind file for the web page and looks like this:

Public Class BookEntry
    Private _author As String
    Private _submitted As Date
    Private _message As String

    Public Property Author() As String
            Return _author
        End Get
        Set(ByVal Value As String)
            _author = Value
        End Set

End Property

    Public Property Submitted() As Date
            Return _submitted
        End Get
        Set(ByVal Value As Date)
            _submitted = Value
        End Set
    End Property

    Public Property Message() As String
            Return _message
        End Get
        Set(ByVal Value As String)
            _message = Value
        End Set
    End Property
End Class

The GridView uses a single template column, which fishes out the values it needs to display. Here's what it looks like (without the style details):

<asp:GridView ID="GuestBookList" runat="server" AutoGenerateColumns="False">
      <asp:TemplateField HeaderText="Guest Book Comments">
          Left By:
          <%# Eval("Author") %>
          <br />
          <b><%# Eval("Message") %></b>
          <br />
          Left On:
          <%# Eval("Submitted") %>


It also adds some style information that isn't included here (because it isn't necessary to understand the logic of the program). In fact, these styles were applied in Visual Studio using the GridView's Auto Format feature.

As for the entries, the guest book page uses the subfolder (App_Data\GuestBook) to store a collection of files. Each file represents a separate entry in the guest book. By putting the GuestBook folder in the App_Data folder, the web application ensures that the user can't access any of the guest book files directly, because the web server won't allow it. However, an even better approach would usually be to create a GuestBook table in a database and make each entry a separate record.

The code for the web page is as follows:

Public Partial Class GuestBook
    Inherits System.Web.UI.Page

Private guestBookName As String

    Protected Sub Page_Load(ByVal sender As Object, _
      ByVal e As EventArgs) Handles MyBase.Load
        guestBookName = Server.MapPath("~/App_Data/GuestBook")

        If Not Me.IsPostBack Then
            GuestBookList.DataSource = GetAllEntries()
        End If
    End Sub

    Protected Sub cmdSubmit_Click(ByVal sender As Object, _
      ByVal e As EventArgs) Handles cmdSubmit.Click
        ' Create a new BookEntry object.
        Dim newEntry As New BookEntry()
        newEntry.Author = txtName.Text
        newEntry.Submitted = DateTime.Now
        newEntry.Message = txtMessage.Text

        ' Let the SaveEntry procedure create the corresponding file.
        Catch err As Exception
            ' An error occurred. Notify the user and don't clear the
            ' display.
            lblError.Text = err.Message & " File not saved."
            Exit Sub
        End Try

        ' Refresh the display.
        GuestBookList.DataSource = GetAllEntries()

        txtName.Text = ""
        txtMessage.Text = ""
    End Sub

    Private Function GetAllEntries() As List(Of BookEntry)
        ' Return a collection that contains BookEntry objects
        ' for each file in the GuestBook directory.
        ' This method relies on the GetEntryFromFile method.
        Dim entries As New List(Of BookEntry)()

            Dim guestBookDir As New DirectoryInfo(guestBookName)

            For Each fileItem As FileInfo In guestBookDir.GetFiles()
                    ' An error occurred when calling GetEntryFromFile().
                    ' Ignore this file because it can't be read.
End Try


        Catch err As Exception
            ' An error occurred when calling GetFiles().
            ' Ignore this error and leave the entries collection empty.
        End Try

        Return entries
    End Function

    Private Function GetEntryFromFile(ByVal entryFile As FileInfo) _
      As BookEntry
        ' Turn the file information into a BookEntry object.
        Dim newEntry As New BookEntry()
        Dim r As StreamReader = entryFile.OpenText()
        newEntry.Author = r.ReadLine()
        newEntry.Submitted = DateTime.Parse(r.ReadLine())
        newEntry.Message = r.ReadLine()

        return newEntry
    End Function

    Private Sub SaveEntry(ByVal entry As BookEntry)
        ' Create a new file for this entry, with a file name that should
        ' be statistically unique.
        Dim random As New Random()
        Dim fileName As String = guestBookName & "\"
        fileName &= DateTime.Now.Ticks.ToString() & random.Next(100).ToString()
        Dim newFile As New FileInfo(fileName)
        Dim w As StreamWriter = newFile.CreateText()

        ' Write the information to the file.
    End Sub

End Class


4.1. Dissecting the Code . . .
  • The code uses text files so you can easily review the information on your own with Notepad. You could use binary files just as easily, which would save a small amount of space.

  • The file name for each entry is generated using a combination of the current date and time (in ticks) and a random number. Practically speaking, this makes it impossible for a file to be generated with a duplicate file name.

  • This program uses error handling to defend against possible problems. However, errors are handled in a different way depending on when they occur. If an error occurs when saving a new entry in the cmdSubmit_Click() method, the user is alerted to the problem, but the display is not updated. Instead, the user-supplied information is left in the controls so the save operation can be reattempted. When reading the existing files in the cmdGetAllEntries_Click() method, two problems can occur, and they're dealt with using separate exception blocks. A problem can happen when the code calls GetFiles() to retrieve the file list. In this situation, the problem is ignored but no files are found, and so no guest book entries are shown. If this step succeeds, a problem can still occur when reading each file in the GetEntryFromFile() method. In this situation, the file that caused the problem is ignored, but the code continues and attempts to read the remaining files.


    The error handling code in this example does a good job of recovering from the brink of disaster and allowing the user to keep working, when it's possible. However, the error handling code might not do enough to alert you that there's a problem. If the problem is a freak occurrence, this behavior is fine. But if the problem is a symptom of a deeper issue in your web application, you should know about it.

    To make sure that problems aren't overlooked, you might choose to show an error message on the page when an exception occurs. Even better, your code could quietly create an entry in the event log that records the problem . That way, you can find out about the problems that have occurred and correct them later.

  • Careful design makes sure this program isolates file writing and reading code in separate functions, such as SaveEntry(), GetAllEntries(), and GetEntryFromFile(). For even better organization, you could move these routines in a separate class or even a separate component. This would allow you to use the ObjectDataSource to reduce your data binding code. 

Top 10
Review : Sigma 24mm f/1.4 DG HSM Art
Review : Canon EF11-24mm f/4L USM
Review : Creative Sound Blaster Roar 2
Review : Philips Fidelio M2L
Review : Alienware 17 - Dell's Alienware laptops
Review Smartwatch : Wellograph
Review : Xiaomi Redmi 2
Extending LINQ to Objects : Writing a Single Element Operator (part 2) - Building the RandomElement Operator
Extending LINQ to Objects : Writing a Single Element Operator (part 1) - Building Our Own Last Operator
3 Tips for Maintaining Your Cell Phone Battery (part 2) - Discharge Smart, Use Smart
- First look: Apple Watch

- 3 Tips for Maintaining Your Cell Phone Battery (part 1)

- 3 Tips for Maintaining Your Cell Phone Battery (part 2)
- How to create your first Swimlane Diagram or Cross-Functional Flowchart Diagram by using Microsoft Visio 2010 (Part 1)

- How to create your first Swimlane Diagram or Cross-Functional Flowchart Diagram by using Microsoft Visio 2010 (Part 2)

- How to create your first Swimlane Diagram or Cross-Functional Flowchart Diagram by using Microsoft Visio 2010 (Part 3)
Popular Tags
Microsoft Access Microsoft Excel Microsoft OneNote Microsoft PowerPoint Microsoft Project Microsoft Visio Microsoft Word Active Directory Biztalk Exchange Server Microsoft LynC Server Microsoft Dynamic Sharepoint Sql Server Windows Server 2008 Windows Server 2012 Windows 7 Windows 8 Adobe Indesign Adobe Flash Professional Dreamweaver Adobe Illustrator Adobe After Effects Adobe Photoshop Adobe Fireworks Adobe Flash Catalyst Corel Painter X CorelDRAW X5 CorelDraw 10 QuarkXPress 8 windows Phone 7 windows Phone 8