Calling All Beta Testers! MyCatchLog Needs Help!

MyCatchLog: Log a trip

I have a fun little project a friend of mine and I have worked on for a bit of time now. The basic premise is a website geared towards avid anglers and charter guides/captains.

You simply register (it really does only take a few seconds and it is completely free), and then begin logging your fishing trips. The data that gets collected can be used to help you identify your most productive fishing spots, most caught species, best bait used, etc. and can all be constrained to specific locations, time of day or year, species, and much more.

The more data you enter, the more you get out of the reporting functionality. Most avid anglers keep some type of logbook; this takes that concept and brings it to the next level.

For technology, it uses Microsoft’s Virtual Earth for mapping and GPS plotting and most pages are AJAX. There are a few places where webservices are called to gather external data.

Unfortunately, this project has been a side project where much of the work is done late at night or for an hour or two here and there. Therefore, there are a few bugs and there was not as comprehensive planning done in the early stages of development. The biggest issue, is that I simply need more traffic and feedback as to how members would like or dislike the website.

If you would like to help, please head on over to MyCatchLog and check it out. Feedback would greatly be appreciated.

Things To Do On a Rainy Day – Create Visual Studio Code Snippets

Code Snippets

I’d like to write a few blog posts sprinkled in regarding great features, practices, methodologies, and other goodies that are often overlooked when under the gun to develop a project, but by knowing about them, and occasionally executing them, you can actually save time, frustration, and produce much greater code.

Structured code snippets fall into the category of writing code for the purpose of writing better code. The simple act of writing code that doesn’t directly grow the project you are working on is why code snippets often aren’t created. In an ideal world however, I believe code snippets are an invaluable asset to maintain code consistency and reusability.

Code Snippet Library

Some programmers maintain code snippet libraries that they reuse for all of their projects. There are a ton of advantages to this, but I do want to add a piece of advice. If your company has it’s own library share it amongst all of your developers so everyone begins writing code exactly the same way. This increases the maintainability of code as well as helping in distribute work-load across employees. If you work solo, my advice would be to create your own library. Don’t snag one from some site because 99/100 times I believe most developers don’t use it. If you create the code snippets yourself (and they’re easy to do), then you are much more likely to remember to use them when the time comes (and what’s the point in having it if you don’t remember to use it).

Project-specific Code Snippets

Another great way to use code snippets is when creating a brand new project. Distribute these code snippets to all developers working on this project and you will notice that your code will be much more consistent from the start. Even if you are working by yourself, some times you have a tendency to slightly change or leave out pieces of code (especially at the wee hours in the morning, if you’re like me). Code snippets helps protect against this.

An Example

The following is a short example I use once in a while when writing class libraries.

<CodeSnippets
    xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
    <CodeSnippet Format="1.0.0">
        <Header>
          <Title>My Class Outline</Title>
          <Author>Nicholas Barger</Author>
          <Description>A basic outline of (nearly) every object class I create.</Description>
        </Header>
        <Snippet>
          <References>
              <Reference>
                  <Assembly>System.Windows.Forms.dll</Assembly>
              </Reference>
          </References>
          <Declarations>
            <Literal>
              <ID>id</ID>
              <ToolTip>Unique ID</ToolTip>
              <Default>int</Default>
            </Literal>
            <Literal>
              <ID>class</ID>
              <ToolTip>Name of the Class</ToolTip>
              <Default>classname</Default>
            </Literal>
          </Declarations>
          <Code Language="CSharp">
              <![CDATA[#region "Internal Variables"

        #endregion

        #region "Properties"

        #endregion

        #region "Constructors"
        public $class$()
        {
            //scratch constructor
        }

        public $class$(int $id$)
        {
            LoadData($id$);
        }

        public void LoadData(int $id$)
        {
            //Load from DAL
        }
        #endregion]]>
            </Code>
        </Snippet>
    </CodeSnippet>
</CodeSnippets>
    

The only thing to note here is that you want to begin writing your code snippet between the …CDATA[ and ] and to create declarations for areas which need to be filled in (denoted by the $id$ or $class$) – these are found in the <Declarations /> section.

There are of course more advanced things you can do with code snippets, but let’s keep it basic for the sake of brevity (homework).

How To Use Code Snippets

Once you’ve created a code snippet just drop it into the appropriate directory, usually something like: Visual Studio 2008\Code Snippets\Visual C#\My Code Snippets or similar. Finally, within Visual Studio, go to an appropriate area such as a new class file in the above example and press Ctrl-K, Ctrl-X; you can now navigate through the dropdown menu of code snippets to find and execute your own. Areas marked in green are your declarations and remind developers to change those values.

Hope this helps and gives you something to do before that next big project, or on a slow rainy day!

An Example of Writing Your Own Blog Engine (mini-version) Part Three

This is a three part series, if you haven’t already read part one, you may want to start there (An Example of Writing Your Own Blog Engine (mini-version)), or part two (An Example of Writing Your Own Blog Engine (mini-version) Part Two).

Presentation Layer

Ok, well this will be the last piece to the puzzle and we’re going to start with the main thread which will house the featured blog post itself as well as a mechanism for handling comments. We want to make it in such a way that we can easily control the number of blog posts that are displayed. For example, if we wanted to display one featured post (the most recent in this example), or allow a user to select a month and display all blog posts for that month. It seems to me that a repeater sounds like a nice, flexible, .NET control to handle this situation. 
Here is the following HTML I used, you can of course change this to however fits
your needs.

HTML and ASP.NET Controls

<asp:Repeater ID="rptMainList" runat="server">
    <ItemTemplate>
        <!-- Blog date -->
        <div><asp:Literal ID="litPublishDate" runat="server" Text='<%#Eval("PubDate").ToLongDateString()%>'></asp:Literal></div>
        <hr />

        <h1><asp:Literal ID="litBlogTitle" runat="server" Text='<%#Eval("Title")%>'></asp:Literal></h1>

        <asp:Literal ID="litBlogPost" runat="server" Text='<%#Eval("Description")%>'></asp:Literal>
        
        <asp:HiddenField ID="hdnBlogID" runat="server" Value='<%#Eval("BlogID")%>' />
        
        <hr />

        <!-- Blog: Add a comment -->
        <table cellpadding="2" cellspacing="4" width="100%">
            <tr>
                <td style="width: 35%;">Enter your name:</td>
                <td style="width: 65%;">Have a comment? Post it here.</td>
            </tr>
            <tr>
                <td><input type="text" id="tbName" runat="server" style="width: 100%;" /></td>
                <td rowspan="5" style="vertical-align: top;"><textarea id="tbComment" runat="server" cols="1" rows="1"></textarea></td>
           </tr>
            <tr>
                <td>Enter your email:</td>
            </tr>
            <tr>
                <td><input type="text" id="tbEmail" runat="server" style="width: 100%;" /></td>
            </tr>
            <tr>
                <td>Enter your URL here:</td>
            </tr>
            <tr>
                <td><input type="text" id="tbURL" runat="server" style="width: 100%;" /></td>
            </tr>
            <tr>
                <td colspan="2" style="text-align: right;">
                    <asp:LinkButton ID="btnPostComment" runat="server" Text="add comment" CommandName="PostComment"></asp:LinkButton>
                </td>
            </tr>
        </table>

        <hr />

        <!-- Blog: Previous comments -->
        <asp:UpdatePanel ID="upnlBlogComments" runat="server">
        <Triggers>
            <asp:AsyncPostBackTrigger ControlID="btnPostComment" EventName="Click" />
            <asp:AsyncPostBackTrigger ControlID="rptMainList" EventName="ItemCommand" />
        </Triggers>
        <ContentTemplate>
            <asp:Repeater ID="rptBlogComments" runat="server" OnItemDataBound="rptBlogComments_ItemDatabound">
                <HeaderTemplate>
                    <table cellpadding="2" cellspacing="4" width="100%" class="comments">
                        <tr>
                            <td colspan="2"><h3 style="padding-bottom: 0px; margin-bottom: 0px;"><span style="color: Black;"><asp:Label ID="lblNumberOfComments" runat="server"></asp:Label></span> Comments</h3></td>
                        </tr>
                </HeaderTemplate>
                <ItemTemplate>
                        <tr>
                            <td>
                                <asp:Label ID="lblDate" runat="server"></asp:Label><br />
                                <asp:Label ID="lblPoster" runat="server"></asp:Label><br />
                                <asp:HyperLink ID="lnkURL" runat="server"></asp:HyperLink>
                            </td>
                            <td style="padding-left: 25px;">
                                <asp:Label ID="lblComment" runat="server" Text=""></asp:Label>
                            </td>
                        </tr>
                </ItemTemplate>
                <AlternatingItemTemplate>
                        <tr>
                            <td colspan="2"><hr /></td>
                        </tr>
                        <tr>
                            <td>
                                <asp:Label ID="lblDate" runat="server"></asp:Label><br />
                                <asp:Label ID="lblPoster" runat="server"></asp:Label><br />
                                <asp:HyperLink ID="lnkURL" runat="server"></asp:HyperLink>
                            </td>
                            <td style="padding-left: 25px;">
                                <asp:Label ID="lblComment" runat="server" Text=""></asp:Label>
                            </td>
                        </tr>
                        <tr>
                            <td colspan="2"><hr /></td>
                        </tr>
                </AlternatingItemTemplate>
                <FooterTemplate>
                    </table>
                </FooterTemplate>
            </asp:Repeater>
        </ContentTemplate>
        </asp:UpdatePanel>
    </ItemTemplate>
</asp:Repeater>

VB Code for Handling the Main Post

Now, we also need a bit of code to handle working with the blog post itself, we’ll reference what we created in the business layer to do this. Notice the use of a helper function to handle repetive code, this is good practice to reduce redundant code making maintenance easier. Also, I prefer to standardize the way I title these types of functions, it will also help in ease of maintenance.

Private _blogbll As BlogBLL

Public Property BlogAdapter() As BlogBLL
    Get
        If _blogbll Is Nothing Then
            _blogbll = New BlogBLL
        End If

        Return _blogbll
    End Get
    Set(ByVal value As BlogBLL)
        _blogbll = value
    End Set
End Property

''' <summary>
''' Get's most recent blog post.
''' </summary>
''' <remarks></remarks>
Private Sub GetBlogPost()
    Dim blogPostDT As Blog.BlogPostsViewDataTable = BlogAdapter.GetMostRecentBlogPost()

    Try
        GetBlogPost_Helper(blogPostDT)
    Catch ex As Exception
        'If this fails, it means there are no blog posts in database at all
        'Should maybe redirect to a plain / non-blog page
        Exit Sub
    End Try
End Sub

''' <summary>
''' Get's specific blog post.
''' </summary>
''' <param name="BlogID"></param>
''' <remarks></remarks>
Private Sub GetBlogPost(ByVal blogID As Integer)
    Dim blogPostDT As Blog.BlogPostsViewDataTable = BlogAdapter.GetBlogPost(blogID)
    GetBlogPost_Helper(blogPostDT)
End Sub

''' <summary>
''' Get's all blog posts for a given month/year (archived posts)
''' </summary>
''' <param name="month"></param>
''' <param name="year"></param>
''' <remarks></remarks>
Private Sub GetBlogPost(ByVal month As Integer, ByVal year As Integer)
    Dim blogPostDT As Blog.BlogPostsViewDataTable = BlogAdapter.GetBlogPosts(month, year)
    GetBlogPost_Helper(blogPostDT)
End Sub

Private Sub GetBlogPost_Helper(ByVal blogPostDR As Blog.BlogPostsViewDataTable)
    rptMainList.DataSource = blogPostDR
    rptMainList.DataBind()
End Sub

Protected Sub rptBlogPosts_ItemDataBound(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.RepeaterItemEventArgs) Handles rptBlogPosts.ItemDataBound
    Select Case e.Item.ItemType
        Case ListItemType.Item
            rptBlogPosts_Helper(sender, e)
        Case ListItemType.AlternatingItem
            rptBlogPosts_Helper(sender, e)
    End Select
End Sub

Private Sub rptBlogPosts_Helper(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.RepeaterItemEventArgs)
    Dim lnkBlogPost As HyperLink = DirectCast(e.Item.FindControl("lnkBlogPost"), HyperLink)

    Dim title As String = e.Item.DataItem("Title").ToString().Replace(" ", "_").Replace(".", "").Replace(",", "")

    lnkBlogPost.Text = e.Item.DataItem("Title").ToString()
    'lnkBlogPost.NavigateUrl = String.Format("default.aspx?blogid={0}", e.Item.DataItem("BlogID").ToString())
    lnkBlogPost.NavigateUrl = String.Format("{0}-{1}", e.Item.DataItem("BlogID").ToString(), title)
End Sub

VB Code for Handling Blog Comments

The same needs to be done with the blog comments.

Private Sub rptBlogComments_Helper(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.RepeaterItemEventArgs)
    Dim lblDate As Label = DirectCast(e.Item.FindControl("lblDate"), Label)
    Dim lblPoster As Label = DirectCast(e.Item.FindControl("lblPoster"), Label)
    Dim lnkURL As HyperLink = DirectCast(e.Item.FindControl("lnkURL"), HyperLink)
    Dim lblComment As Label = DirectCast(e.Item.FindControl("lblComment"), Label)

    Dim dateCreated As DateTime
    Dim pubDate As String = "Unknown"
    Try
        dateCreated = e.Item.DataItem("PubDate")
        pubDate = dateCreated.ToLongDateString()
    Catch ex As Exception
    End Try
    lblDate.Text = pubDate

    Dim poster As String = e.Item.DataItem("Username").ToString()
    If String.IsNullOrEmpty(poster) = True Then
        poster = "anonymous"
    End If

    lblPoster.Text = poster
    lblComment.Text = e.Item.DataItem("Comment").ToString()

    Dim url As String = e.Item.DataItem("URL").ToString()
    lnkURL.Text = url
    lnkURL.NavigateUrl = url
End Sub

Protected Sub rptBlogComments_ItemDatabound(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.RepeaterItemEventArgs)
    Select Case e.Item.ItemType
        Case ListItemType.Header
            Dim blogID As Integer = hdnTempBlogID.Value
            Dim lblNumberOfComments As Label = DirectCast(e.Item.FindControl("lblNumberOfComments"), Label)

            lblNumberOfComments.Text = BlogAdapter.GetNumberOfBlogComments(blogID).ToString()
        Case ListItemType.Item
            rptBlogComments_Helper(sender, e)
        Case ListItemType.AlternatingItem
            rptBlogComments_Helper(sender, e)
    End Select
End Sub

Protected Sub rptMainList_ItemCommand(ByVal source As Object, ByVal e As System.Web.UI.WebControls.RepeaterCommandEventArgs) Handles rptMainList.ItemCommand
    Select Case e.CommandName
        Case "PostComment"
            Dim hdnBlogID As HiddenField = DirectCast(e.Item.FindControl("hdnBlogID"), HiddenField)
            Dim tbComment As HtmlTextArea = DirectCast(e.Item.FindControl("tbComment"), HtmlTextArea)
            Dim tbName As HtmlInputText = DirectCast(e.Item.FindControl("tbName"), HtmlInputText)
            Dim tbEmail As HtmlInputText = DirectCast(e.Item.FindControl("tbEmail"), HtmlInputText)
            Dim tbURL As HtmlInputText = DirectCast(e.Item.FindControl("tbURL"), HtmlInputText)

            Dim blogID As Integer = hdnBlogID.Value
            Dim comment As String = tbComment.Value
            Dim username As String = tbName.Value
            Dim email As String = tbEmail.Value
            Dim url As String = tbURL.Value

            Try
                BlogAdapter.CreateBlogComment(blogID, comment, username, email, url)
            Catch ex As Exception
                'FIX add error handling here
            End Try

            'Retrieve blog comments
            Dim rptBlogComments As Repeater = DirectCast(e.Item.FindControl("rptBlogComments"), Repeater)

            rptBlogComments.DataSource = BlogAdapter.GetBlogComments(blogID)
            rptBlogComments.DataBind()
    End Select
End Sub

Protected Sub rptMainList_ItemDataBound(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.RepeaterItemEventArgs) Handles rptMainList.ItemDataBound
    hdnTempBlogID.Value = e.Item.DataItem("BlogID")

    'Retrieve blog comments
    Dim rptBlogComments As Repeater = DirectCast(e.Item.FindControl("rptBlogComments"), Repeater)

    rptBlogComments.DataSource = BlogAdapter.GetBlogComments(e.Item.DataItem("BlogID"))
    rptBlogComments.DataBind()
End Sub

Finally, the Trigger

All the trigger does (in this case on page load) is handle the possible incoming data variations as well as kick off the whole process. As you can see below we have a few variations of data that can be passed in (and more in the real blog and yours as you add to it).

First, no additional data can be passed in and the blog engine will default to the most recent post only. Alternatively, you could pass in the month and year and retrieve a list of blog posts for that month. And finally, you can pass in a blog id and retrieve a specific blog post to read.

Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
    If Page.IsPostBack = False Then
        Dim month As Integer = 0
        Try
            month = Integer.Parse(Request.QueryString("month"))
        Catch ex As Exception
        End Try

        Dim year As Integer = 0
        Try
            year = Integer.Parse(Request.QueryString("year"))
        Catch ex As Exception
        End Try

        Dim blogID As Integer = 0
        Try
            blogID = Integer.Parse(Request.QueryString("blogid"))
        Catch ex As Exception
        End Try

        If month > 0 And year > 0 Then
            GetBlogPost(month, year)
        ElseIf blogID > 0 Then 'Populate requested blog post
            GetBlogPost(blogID)
        Else 'Populate most recent blog post
            GetBlogPost()
        End If
    End If
End Sub

And that’s it! In just three short installments we put together a basis for your very own blog engine which I encourage all of you to build upon, rewrite, improve, and generally play around with. Please leave comments on new ideas for the blog engine, updates to how you’ve enhanced it, or any bugs found within this base (there’s always a few).

An Example of Writing Your Own Blog Engine (mini-version) Part Two

This is a three part series, if you haven’t already read part one, you may want to start there (An Example of Writing Your Own Blog Engine (mini-version)). Also, please check back soon for the final part (presentation layer).

Data Access Layer

We pick up in this article after finishing setting up the database. We now want to go ahead and begin setting up the programmatic access to manipulating data within our newly created database. This can be done several different ways and there are pro’s and con’s to each. Let’s briefly, conceptually, discuss two possible ways of developing data access layers.

First, if you’ve read tutorials from Microsoft, as well as many other sources, you will generally see reference to a more GUI approach to DAL development. Primarily, I am talking about creating a DataSet and then working with .XSD’s. This gives you some obvious advantages such as formalizing the data you will be working with fairly precisely, developing in a very quick manner with wizard-style setup, ease of development is greatly increased for entry level developers, and it provides better visual demonstration of what you’re developing.

Secondly, you could create the entire data access layer in code by creating functions that interact with the database rather than allowing visual studio to develop this code for you. It is more tedious of course, but you have exacting control, complete knowledge of what is happening under the covers, and in my opinion is vastly more scaleable and flexible.

For this demonstration, we will be using the first method of developing a DataSet for our data access. I’m sure there are additional considerations to make when choosing which route to take, that will be part of your analysis and planning phase when developing your RSS engine (or any project).

I’m going to assume you know how to create DataTables, queries, etc. so that will not be covered in this article, however if you are unclear, there are many articles through Microsoft which demonstrate how to use DataSets; please refer to one of these.

BlogPostsView

Blog Posts View

This DataTable will be responsible for showing the primary details of the blog posts, including title, summary, description (which is the post itself), pubdate (date created), and comments count (a count of all comments placed for this blog post).

FillBlogPosts, GetBlogPosts

SELECT BlogID, CommentsCount, PubDate, DateUpdated, Description, Summary, Title
FROM BlogPostsView

CreateBlogPost, DeleteBlogPost

Simply use the corresponding stored procedures (dbo.spCreateBlogPost, dbo.spDeleteBlogPost)

FillBlogPostByBlogPostID, GetBlogPostByBlogID

SELECT BlogID, Title, Summary, Description, PubDate, DateUpdated, CommentsCount
FROM BlogPostsView
WHERE (BlogID = @BlogID)

FillByMostRecentBlogPost, GetMostRecentBlogPost

SELECT TOP (1) BlogID, CommentsCount, PubDate, DateUpdated, Description, Summary, Title
FROM BlogPostsView
ORDER BY PubDate DESC


Blog Comments View

This datatable represents the comments users can add to blog posts.

FillBlogComments, GetBlogComments

SELECT BlogCommentID, BlogID, Username, Email, URL, Comment, DateCreated, DateUpdated
FROM BlogComments
WHERE (BlogID = @BlogID)

GetNumberOfBlogComments

SELECT COUNT(*) FROM BlogComments WHERE BlogID = @BlogID

BlogPostList

Blog Post List

This datatable is responsible for displaying the blog posts in a list form. This is slightly different than the BlogPostView because we’re not retrieving back the actual blog post content, which is a lot heavier of a data pull. By creating a seperate datatable, we’ll remain pretty lean and efficient.

FillBlogPostList, GetBlogPostList

SELECT BlogID, Title, PubDate, CommentsCount
FROM BlogPostsView
ORDER BY PubDate DESC

An additional delete is listed in this view, because I had use for it without switching datatables – this is not really necessary though.

Sidenote: If you wanted to develop this code without using the GUI method, you could create a class that simply has your dataaccess code in there in place of this XSD-style DAL. An example of this could look like the following:

Public Function DeleteBlogPost(ByVal BlogID as Integer) as Boolean
    Dim MyConnection As Data.SqlClient.SqlConnection = New Data.SqlClient.SqlConnection(ConfigurationManager.ConnectionStrings("MyConnectionString").ToString())
    Dim cmd As Data.SqlClient.SqlCommand = New Data.SqlClient.SqlCommand("dbo.spDeleteBlogPost", MyConnection)
    cmd.CommandType = Data.CommandType.StoredProcedure

    cmd.Parameters.AddWithValue("@BlogID ", BlogID)

    Try
        MyConnection.Open()
        cmd.ExecuteNonQuery()
    Catch ex As Exception
        'Error handling
    Finally
        MyConnection.Close()
        cmd = Nothing
    End Try

    Return True
End Function

Business Layer

The following code is used in the business layer. This is very simple code, so I want go into too much detail explaining it; as an overview though, we are simply calling up a layer to the DAL to retrieve, create, edit, or delete data.

One slight trick is to create adapters as properties that can be then called a little bit easier later on, you can see that at the top of the code block.

Finally, you can notice there is some very basic validation being done at this level, it would be good practice to add to this for greater security/control over the data manipulation.

Imports Microsoft.VisualBasic

Imports Blog

Public Class BlogBLL
    Private _blogPostsAdapter As BlogTableAdapters.BlogPostsViewTableAdapter
    Private _blogCommentsAdapter As BlogTableAdapters.BlogCommentsTableAdapter
    Private _blogPostListAdapter As BlogTableAdapters.BlogPostListTableAdapter

    Public Property BlogPostsAdapter() As BlogTableAdapters.BlogPostsViewTableAdapter
        Get
            If _blogPostsAdapter Is Nothing Then
                _blogPostsAdapter = New BlogTableAdapters.BlogPostsViewTableAdapter
            End If

            Return _blogPostsAdapter
        End Get
        Set(ByVal value As BlogTableAdapters.BlogPostsViewTableAdapter)
            _blogPostsAdapter = value
        End Set
    End Property

    Public Property BlogCommentsAdapter() As BlogTableAdapters.BlogCommentsTableAdapter
        Get
            If _blogCommentsAdapter Is Nothing Then
                _blogCommentsAdapter = New BlogTableAdapters.BlogCommentsTableAdapter
            End If

            Return _blogCommentsAdapter
        End Get
        Set(ByVal value As BlogTableAdapters.BlogCommentsTableAdapter)
            _blogCommentsAdapter = value
        End Set
    End Property

    Public Property BlogPostListAdapter() As BlogTableAdapters.BlogPostListTableAdapter
        Get
            If _blogPostListAdapter Is Nothing Then
                _blogPostListAdapter = New BlogTableAdapters.BlogPostListTableAdapter
            End If

            Return _blogPostListAdapter
        End Get
        Set(ByVal value As BlogTableAdapters.BlogPostListTableAdapter)
            _blogPostListAdapter = value
        End Set
    End Property

    Public Function GetBlogPosts() As BlogPostsViewDataTable
        Return BlogPostsAdapter.GetBlogPosts()
    End Function

    Public Function GetBlogPosts(ByVal searchInput As String) As BlogPostsViewDataTable
        Return BlogPostsAdapter.GetBlogPostsBySearchInput(searchInput)
    End Function

    Public Function GetBlogPost(ByVal blogID As Integer) As BlogPostsViewDataTable
        Return BlogPostsAdapter.GetBlogPostsByBlogPostID(blogID)
    End Function

    Public Function GetMostRecentBlogPost() As BlogPostsViewDataTable
        Return BlogPostsAdapter.GetMostRecentBlogPost()
    End Function

    Public Function GetBlogComments(ByVal blogID As Integer) As BlogCommentsDataTable
        Return BlogCommentsAdapter.GetBlogComments(blogID)
    End Function

    Public Function CreateBlogPost(ByVal title As String, _
        ByVal summary As String, _
        ByVal description As String, _
        ByRef blogID As Integer) As Boolean

        'Check that the blog's title is not empty
        If String.IsNullOrEmpty(title) = True Then
            Throw New Exception("Title can not be empty.")
        End If

        'Check that the blog post is not empty
        If String.IsNullOrEmpty(description) = True Then
            Throw New Exception("Blog post can not be empty.")
        End If

        Return BlogPostsAdapter.CreateBlogPost(title, summary, description, blogID)
    End Function

    Public Function UpdateBlogPost(ByVal blogID As Integer, _
        ByVal title As String, _
        ByVal summary As String, _
        ByVal description As String) As Boolean

        'Check that the blog's title is not empty
        If String.IsNullOrEmpty(title) = True Then
            Throw New Exception("Title can not be empty.")
        End If

        'Check that the blog post is not empty
        If String.IsNullOrEmpty(description) = True Then
            Throw New Exception("Blog post can not be empty.")
        End If

        Return BlogPostsAdapter.UpdateBlogPost(blogID, title, description, summary)
    End Function

    Public Function DeleteBlogPost(ByVal blogID As Integer) As Boolean
        If blogID <= 0 Then
            Throw New Exception("Blog ID must be greater than 0.")
        End If

        Return BlogPostsAdapter.DeleteBlogPost(blogID)
    End Function

    Public Function CreateBlogComment(ByVal blogID As Integer, _
        ByVal comment As String, _
        Optional ByVal username As String = Nothing, _
        Optional ByVal email As String = Nothing, _
        Optional ByVal url As String = Nothing) As Boolean

        Return BlogCommentsAdapter.CreateBlogComment(blogID, username, email, url, comment)
    End Function

    Public Function GetNumberOfBlogComments(ByVal blogID As Integer)
        Return BlogCommentsAdapter.GetNumberOfBlogComments(blogID)
    End Function

    Public Function GetBlogPostList()
        Return BlogPostListAdapter.GetBlogPostList()
    End Function
End Class

Ok, that’s it for this part of the series. Please check back later when I demonstrate how we start implenting the presentation layer!

Remember, this is not only good practice for writing a simple application, but it’s also really beneficial to develop the basis of a blog engine that you can now build any custom features you can’t get with out of the box blog services or engines. So, at the end of this series, be sure to take this code and make it your own by adding to it. Feel free to write me back with additional features you’ve come up with in the comments below!

An Example of Writing Your Own Blog Engine (mini-version)

Preface: Obviously blog engines can be much more comprehensive than what I will be describing in this article. This is just something to show a few techniques to get you started.

This is a three part series, please check back soon for the next two parts (business layer and presentation layer).

The Database (Barebones)

In this blog engine, we’re only going to have two tables; blog posts and blog comments. The tables look as follows:

Blog Posts Table

BlogPosts    
BlogID  (PK, int, not null) Internal ID of blog post
Title (varchar(100), not null) The title of the blog post
Summary (varchar(200), null) A brief description of blog post (displayed in rss lists)
Description (varchar(8000), not null) The actual content of the blog post
DateCreated (datetime, not null) Date blog post was created
DateUpdated (datetime, not null) Date blog post was last updated

Note: We're creating this as though it can only have one blog.  This model
will not support multiple blogs without adding a higher level table to track
individual blogs, but that's a pretty simple enhancement you can make.

Blog Comments Table

BlogComments    
BlogCommentID (PK, int, not null) Internal ID of blog post comment
BlogID (FK, int, not null) ID of blog post the comment responds to
Username (varchar(100), null) Username of user commenting (can be anonymous)
Email (varchar(100), null) Email of user commenting (can be anonymous)
URL (varchar(250), null) URL of trackback/additional content
Comment (varchar(2000), not null) The actual content of the blog post comment
DateCreated (datetime, not null) Date comment was created
DateUpdated (datetime, not null) Date comment was last updated (usually never changes)

Note: An additional enhancement you may want to make is to create an approval process of comments. There are several ways to do this, one way could be to simply add a new column in the BlogComments table which toggles status of comment. An administrator could then manually change the status of comments to be approved or not.

Great, database tables are done – not very hard, right? Next, lets go ahead and write a few stored procedures to handle data manipulation.

SPROC’s

We’re going to create four simple stored procedures to handle creating a new blog post, updating an existing blog post, deleting a blog post, and finally, create a blog comment.

CreateBlogPost

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- =============================================
-- Author:	Nicholas Barger
-- Create date: 04/01/2008
-- Description:	Creates a new blog post
-- =============================================
CREATE PROCEDURE [dbo].[spCreateBlogPost] 
	@Title varchar(100),
	@Summary varchar(200),
	@Description varchar(8000),
	@BlogID int output
AS 
BEGIN
	SET NOCOUNT ON;

	INSERT INTO dbo.BlogPosts (Title, Summary, Description) VALUES (@Title, @Summary, @Description);

	SELECT @BlogID = SCOPE_IDENTITY();
END
GO

DeleteBlogPost

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- =============================================
-- Author:	Nicholas Barger
-- Create date: 04/01/2008
-- Description:	Deletes an existing blog post
-- =============================================
CREATE PROCEDURE [dbo].[spDeleteBlogPost] 
	@BlogID int
AS 
BEGIN
	SET NOCOUNT ON;

	DELETE FROM BlogPosts WHERE BlogID = @BlogID;
END
GO

UpdateBlogPost

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- =============================================
-- Author:	Nicholas Barger
-- Create date: 04/01/2008
-- Description:	Updates an existing blog post
-- =============================================
CREATE PROCEDURE [dbo].[spUpdateBlogPost] 
	@BlogID int, 
	@Title varchar(100),
	@Description varchar(8000),
	@Summary varchar(200) = null
AS 
BEGIN
	SET NOCOUNT ON;

	UPDATE dbo.BlogPosts SET DateUpdated = getDate(), Title = @Title, Summary = @Summary, Description = @Description WHERE BlogID = @BlogID;
END
GO

CreateBlogComment

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- =============================================
-- Author:	Nicholas Barger
-- Create date: 04/01/2008
-- Description:	Creates a new blog post comment
-- =============================================
CREATE PROCEDURE [dbo].[spCreateBlogComment] 
	@BlogID int, 
	@Username varchar(100) = null,
	@Email varchar(100) = null,
	@URL varchar(250) = null,
	@Comment varchar(2000),
	@BlogCommentID int output
AS 
BEGIN
	SET NOCOUNT ON;

	INSERT INTO dbo.BlogComments (BlogID, Username, Email, URL, Comment) VALUES (@BlogID, @Username, @Email, @URL, @Comment);
	
	SELECT @BlogCommentID = SCOPE_IDENTITY();
END
GO

Ok, we’re going to stop there for this part of the series. Please check back later when I demonstrate how we start using the database we setup!

Remember, this is not only good practice for writing a simple application, but it’s also really beneficial to develop the basis of a blog engine that you can now build any custom features you can’t get with out of the box blog services or engines. So, at the end of this series, be sure to take this code and make it your own by adding to it. Feel free to
write me back with additional features you’ve come up with in the comments below!

Oh, Bitter Frustrations…

ACL Injury

There’s been quite a delay in my blog entries as of late. Just when I was getting on a roll as well. Unfortunately I’ve
experienced a series of unfortunate events.

Warning: This post really has little to do with any technology information and provides very little useful information whatsoever; but today i’ll vent just a bit.

The Original Problem

It started while at a friend’s house when in a freak accident I tore my Anterior Crucial Ligament (ACL). For those of
you sport buffs (or medically trained) who recognize that term know that I am “out for the season”. Looks like
I won’t be able to become a professional football player after all. As far as I understand the ACL is the main tendon/ligament
that stabilizes the knee.

Accidents happen, and I don’t begrudge any incidents or sulk on the issue; but it is amazing the eye-opening experience
of all the little nuances we realize when experiencing an injury like this. Pain is obvious, but most people can overcome
it, either by toughness or medication. The real awkwardness comes from logistical problems. I feel obliged to cover this
in great detail further down in this article, please bare with me as I continue the timeline.

Diagram of Knee

Recovery, sort of.

In my case I took a week or so to recover from the original injury, get off the crutches, etc. I was however left in a
sport brace and with quite an awkward limp as well as a bit of loss of motion in my leg/knee.

A Decision

You have to make a decision, do you want to have surgery (which is a long and painful process requiring approximately nine months
to a year for full recovery), or give up the possibility of playing sports, even recreational sports, and possibly reinjuring
throughout the rest of your life to simply leave the knee as is. I decided that I’m young, I love sports, and I want to be
able to play with my kids (when I have them someday) for as long as possible. I opted for the surgery.

The Surgery

Now, agreeing to the surgery and thinking single mindedly of the end-result is great, though it is a bit lacking in
thoroughly understanding what all is involved.

First, there is surgery, which all surgery has a degree of risk. Second, there is the pain from surgery; which though I
consider myself to be of high pain tolerance, is certainly no picnic. Third, pain medicine sometimes does not agree with
certain people (me) and causes extreme nausea. And finally, all of the additional logistical concerns.

Logistics

Well, I’ve alluded to it a few times now, but it is the single most frustrating aspect of injury that is always overlooked.
I honestly think that there is something in your brain, perhaps a defense mechanism, that causes you to overlook and forget these
frustrations.

Wearing a leg-brace from ankle to groin that is locked in a fixed straight position is ridiculous; advantageous to the healing
process but ridiculous to all other aspects of life. It’s like carrying around deadweight that jolts out at awkward angles to
your body. Getting into a car (only the passenger side) is a chore unto itself, driving is of course out of the question.
Rashes from the brace make you irritable as well as self conscious. Going to the restroom is difficult. Being forced to sleep
in a single position is difficult. Most people wouldn’t think about the pressure on your heels laying on your back causes.
Additionally, the brace can causes bruising itself; tight bands to keep you locked in place on the injured extremity as well
as bruising pressure points against the non-operated leg. The stiffness of your body, headaches from laying in bed so long as well as neck and back pains.

Crutches are an entire set of additional problems. Try to carry an open drink anywhere, how about a plate of food, how
about pretty much anything; not impossible, but very slow and difficult.

Losing the freedom to depend solely on yourself is mentally draining. Friends, family, and even strangers can be wonderful,
but of course you inevitably feel like a burden on them; especially those who love you and help you the most.

There are of course an unenviable infinite list of other small and large frustrations, but I do think it’s important to
realize, that it could be worse. That I am absolutely lucky that the pain and difficulty you go through during surgery is
often to make things better in the long run, and the simple fact that you have an option to make things better is worth
everything and warrants appreciation.

Resolution

Still to come… but I am back to a place where I can begin working, writing, and living again; and for that I am
greatful and excited!