Monday, June 10, 2013

Extending Objects in ASP.Net (In code behind)

The good news is that it is extremely easy to extend any object in the .Net Framework.  Actually, that is misleading.  It isn’t the framework that lets you extend objects in code behind, it is the CLS.
*** Update - This should be CLR.  Fat fingered typo. ***
In order to understand how it works, you need to understand the difference between a class and a module.  They are both structures to create objects but a class is pretty open as compared to a module.  A module can not implement nor can it inherit.  Think of a module as a self-contained object.  It can survive by itself without any help.  Additionally, by its nature, the methods inside a module are Public and Shared.  One caveat to my description of a module is that you can import objects, for instance -
Imports Microsoft.VisualBasic
Imports System.Xml




In order to extend an object, you must give a compiler directive -



<System.Runtime.CompilerServices.Extension()>




This lets the compiler know that you are looking to extend an existing object.  However, (In VB.Net) you can not issue this directive outside of a Module. 


Lets look at a real world example.  Perhaps you have a public facing website that takes feedback from users.  You are concerned about your younger audience seeing inappropriate posts made by older members.  You could write a class and have it instantiated before calling it and finally calling it –or- you could extend the string data type to include your sanitizer.  Why would we do this?  So that we could reuse our code and not have to instantiate an object everytime we wanted to sanitize text.


Once you understand the concept, you will wonder how you ever lived without it and start creating your own library of extensions (I have a couple hundred for everything from replacing the functionality of Lstr to getting the last business day of a month).


Lets get started by opening VS2010 and firing off a new empty web site.  We don’t need anything for this example so all that pre-loaded Microsoft templated crap is unnecessary.  Now we need to add some foundation to support our example.  Right click on your Solution | Add ASP.Net Folder | App_Code.  This will add the Asp.Net reserved folder to your solution.  Now right click on your App_Code folder | Add New Item… | Choose Class.  Name the Class Extensions and then click ok. 
You can pretty much delete everything contained in our new class as it does not serve our purpose.  Remember we are creating a Module.  So once you have highlighted and deleted everything in the file, the first thing we want to do is create a namespace for our module -




Namespace Extensions
End Namespace




Once we have that we will need to name the Module and declare it.  In this example you will see how I hide the module name with a compiler directive called HideModuleName -



Namespace Extensions
    <HideModuleName()> _
    Public Module Extensions    End Module
End Namespace




This makes it easier to reference the module in code.  It is not necessary, but I usually include it unless there is a conflicting namespace module.  Now we need to create the method that will serve as our extension.  This will do the dirty work of what we are trying to accomplish. 
Lets think about what we are trying to accomplish for a second.  We want to make sure there are no cuss words in a given string and if there is, we need to strip and then replace the word with something else.  In my example, I create the list of cuss words but ideally you would want to get this list from an updatable source such as SQL -




Public Function cussWordSanitizer(ByVal source As String) As String
     'Ideally you would create this list from a datasource (IE - SQL)
     Dim cussList() As String = New String() {"poopie", "doodie", "toilet"}
     Dim replaceText As String = New String("(I am a potty mouth)")

     For Each cussword As String In cussList
         If (source.Contains(cussword)) Then
             source = source.Replace(cussword, replaceText)
         End If
     Next
     Return source
End Function




This represents our cuss word sanitizer that protects the youth members from the evils of ignorant language.  Basically, we receive our inputted string, we build an array of cuss words (here I have 3), and then create a replacement string in case we actually find a cuss word.  Then we check our string to see if it contains any of the cuss words.  If it does, then we will need to replace it with our replacement string.

One last thing we need to do is add our directive to the method to let the compiler know we expect to use this as an extension -




<System.Runtime.CompilerServices.Extension()>




This needs to be place right before your method declaration, like so -



<System.Runtime.CompilerServices.Extension()> _
Public Function cussWordSanitizer(ByVal source As String) As String
     'Ideally you would create this list from a datasource (IE - SQL)
     Dim cussList() As String = New String() {"poopie", "doodie", "toilet"}
     Dim replaceText As String = New String("(I am a potty mouth)")

     For Each cussword As String In cussList
         If (source.Contains(cussword)) Then
             source = source.Replace(cussword, replaceText)
         End If
     Next
     Return source
End Function




So our entire class (module) would look like -



Namespace Extensions
    <HideModuleName()> _
Public Module Extensions
        <System.Runtime.CompilerServices.Extension()> _
   Public Function cussWordSanitizer(ByVal source As String) As String
       'Ideally you would create this list from a datasource (IE - SQL)
       Dim cussList() As String = New String() {"poopie", "doodie", "toilet"}
       Dim replaceText As String = New String("(I am a potty mouth)")

       For Each cussword As String In cussList
           If (source.Contains(cussword)) Then
               source = source.Replace(cussword, replaceText)
           End If
       Next
       Return source
   End FunctionEnd Namespace




It is extremely important to remember to put the continuation character at the end of your directive.   This is what tells the compiler which method you plan to use as your extension.  The continuation character is the under bar (_).


Now that we have the module in order, we need to create the front end.  Add a Default.aspx web form to your project (This article assumes you know how to add a web form to your project).  Now we need to be able to accept user input and then a trigger to actuate the sanitizer.  Finally, we need a place to display our sanitized text.  So we need a textbox, button, and then label.  My code looks like -



Enter Sentence: <asp:TextBox ID="txtCussTester" runat="server" />
<br />
<asp:Button ID="btnCussTester" Text="Press Me To Test!" runat="server" />
<br />
<asp:Label ID="lblCussTester" runat="server" />




My entire <body> block looks like -



<body>
<form id="form1" runat="server">
<div>Enter Sentence: <asp:TextBox ID="txtCussTester" runat="server" />
<br />
<asp:Button ID="btnCussTester" Text="Press Me To Test!" runat="server" />
<br />
<asp:Label ID="lblCussTester" runat="server" /></div>
</form>
</body>




Now we need to switch to the code behind.  First thing you need to do is import the namespace where our module is located -



Imports Extensions




Now we need to access our trigger, which we will use the button’s click event for.  There we will then collect the string, pass it through our sanitizer and then display it in our label. So all together our code will look like -



Dim text As String = txtCussTester.Text
lblCussTester.Text = text.cussWordSanitizer()




Or in short form we could simply say -



lblCussTester.Text = txtCussTester.Text.cussWordSanitizer()


Either way it works the same.  Lets go ahead and run this -

image


Here we have our field and our button.

image


Here we have nothing to sanitize.

image


Here we have sanitized the text to make it age appropriate.


To prove that we can extend any object, lets go ahead and extend a DateTime object.  In this instance, we are going to provide a datetime.now (or the current date/time) and run some method and then get the last business day of the month back (hopefully).


We need to add another method to our extensions.  So open your Extensions.vb file.  We need to add the following method -



<System.Runtime.CompilerServices.Extension()> _
        Public Function lastBusinessDay(ByVal dt As DateTime) As DateTime
            Dim LastOfMonth As DateTime
            LastOfMonth = New DateTime(dt.Year, _
                          dt.Month, DateTime.DaysInMonth(dt.Year, dt.Month))

            If LastOfMonth.DayOfWeek = DayOfWeek.Sunday Then
                lastBusinessDay = LastOfMonth.AddDays(-2)
            ElseIf LastOfMonth.DayOfWeek = DayOfWeek.Saturday Then
                lastBusinessDay = LastOfMonth.AddDays(-1)
            Else
                lastBusinessDay = LastOfMonth
            End If

            Return lastBusinessDay
        End Function




So now, our entire Extensions.vb module will look like -



Namespace Extensions
    <HideModuleName()> _
Public Module Extensions
        <System.Runtime.CompilerServices.Extension()> _
        Public Function cussWordSanitizer(ByVal source As String) As String
            'Ideally you would create this list from a datasource (IE - SQL)
       Dim cussList() As String = New String() {"poopie", "doodie", "toilet"}
            Dim replaceText As String = New String("(I am a potty mouth)")

            For Each cussword As String In cussList
                If (source.Contains(cussword)) Then
                    source = source.Replace(cussword, replaceText)
                End If
            Next
            Return source
        End Function
        <System.Runtime.CompilerServices.Extension()> _
        Public Function lastBusinessDay(ByVal dt As DateTime) As DateTime
            Dim LastOfMonth As DateTime
            LastOfMonth = New DateTime(dt.Year, _
                          dt.Month, DateTime.DaysInMonth(dt.Year, dt.Month))

            If LastOfMonth.DayOfWeek = DayOfWeek.Sunday Then
                lastBusinessDay = LastOfMonth.AddDays(-2)
            ElseIf LastOfMonth.DayOfWeek = DayOfWeek.Saturday Then
                lastBusinessDay = LastOfMonth.AddDays(-1)
            Else
                lastBusinessDay = LastOfMonth
            End If

            Return lastBusinessDay
        End Function
    End Module
End Namespace




We need to add a Label control to the Default.aspx file, like so -



<body>
<form id="form1" runat="server">
<div>Enter Sentence: <asp:TextBox ID="txtCussTester" runat="server" />
<br />
<asp:Button ID="btnCussTester" Text="Press Me To Test!" runat="server" />
<br />
<asp:Label ID="lblCussTester" runat="server" />
<br />
<asp:Label ID="lblLastBusinessDay" runat="server" /></div>
</form>
</body>




Finally, we need to add it to our code behind in Default.aspx.vb file -



Protected Sub btnCussTester_Click(sender As Object, e As System.EventArgs) _
                                                Handles btnCussTester.Click
        Dim start As DateTime = DateTime.Now
        Dim text As String = txtCussTester.Text
        lblCussTester.Text = text.cussWordSanitizer()

        lblLastBusinessDay.Text = "The Last Business Day Is: " & _
            DateTime.Now().lastBusinessDay().Day.ToString() & _
            "th."End Sub




This gives us the following result -

image


Which today is 06/06/2013 which makes the 28th the correct answer.

Have fun extending.


Håþþ¥ .ñꆆïñg…

1 comment: