Double Event Execution in global.asax

The native events in global.asax will autowireup no matter what you do. If you explicitly handle the event, it will execute from the autowireup and from the Handles statement. Hilarity will ensue.

So to be clear, this is bad:

Private Sub Application_PreRequestHandlerExecute(ByVal sender As Object, ByVal e As EventArgs) Handles Me.PreRequestHandlerExecute
End Sub

This is good:

Private Sub Application_PreRequestHandlerExecute(ByVal sender As Object, ByVal e As EventArgs)
End Sub
Advertisements

Optional Parameters

In theory, optional parameters can be useful.  If I have a public method, and I want to add a new parameter, I can add it with the Optional keyword.

Public Sub MyFunction(ByVal parameter1 As Integer, Optional ByVal parameter2 As Integer = 0)
End Sub

I’ve added a new bit of functionality, the existing code still works, life is good.

But optional parameters have a darker, more sinister side.  They limit my options.  What if I want to add a new, required parameter to that method?  I can follow the original pattern and add it at the end with the Optional keyword (not that I have a choice, once Optional, always Optional — anything to the right of an Optional parameter must also be Optional):

Public Sub MyFunction(ByVal parameter1 As Integer, Optional ByVal parameter2 As Integer = 0, Optional ByVal parameter3 As Integer = 0)

End Sub

Except now it’s not required, it’s optional.  That’s no good.  Never fear, I’ll just wedge it in before the Optional parameter:

Public Sub MyFunction(ByVal parameter1 As Integer, ByVal parameter3 As Integer, Optional ByVal parameter2 As Integer = 0)

End Sub

Life is good, yes?  No.  What if I was calling the method like this:

Public Sub MyCallingMethod()
    MyFunction(1, 2)
End Sub

Even after adding parameter3 in the middle, this will still compile.  Maybe if we’re lucky QA will stumble across the hidden bug, although I wouldn’t bet my weekend on it.  (And you will be betting a weekend on it.)

If this method were Private, so you have total control and full visibility on where it’s called from, you might get away with adding a parameter in the middle of the list.  You’ll need to be very thorough to get away with it.

Only in the most dire circumstances should you consider this sort of modification in a Public method that could be called from some other code project.  The risk is just not acceptable.  The Optional parameters were a bad enough decision (but that’s a separate email!) – don’t multiply the magnitude of the problem.

The simplest solution is to avoid the issue by avoiding Optional parameters whenever possible.  Failing that, your back up plan is to leave the original method alone.

Public Sub MyFunction(ByVal parameter1 As Integer, Optional ByVal parameter2 As Integer = 0)

End Sub

Then add a new method with a new name and the new parameter added.

Public Sub MyFunctionWithParam3(ByVal parameter1 As Integer, ByVal parameter3 As Integer, Optional ByVal parameter2 As Integer = 0)

End Sub

New method name – no conflicts with any legacy code.

DateDiff

If you want to find the number of days difference between two DateTime values, there’s a couple ways you can do it.

You can cast the values to a String (formatted to strip off the time), then cast them back to a DateTime, then subtract them, for a total of five operations:

Dim resultFromTheBADWay As Integer = Convert.ToDateTime(Now.ToShortDateString()).Subtract(oldestCaseAction.StartDate.ToShortDateString()).Days

You can leave the values in their native DateTime format and use a single native Date operation:

Dim resultFromTheGOODWay As Integer = DateDiff(DateInterval.Day, Now, oldestCaseAction.StartDate)

Use your judgment on determining the better option for this scenario, as long as you use the second method.

Nullable Types

Wrapping settings retrieved from the database in a Property and storing them in Private local variables is a good thing. If the variable is still set to its default value, we populate from the database. Good times.

Private Shared _someSetting As Integer = 0
Public Shared ReadOnly Property SomeSetting As Integer
    Get
        If _someSetting = 0 Then
            _someSetting = GetSomethingFromTheDatabase()
        End If
        Return _someSetting
    End Get
End Property

But what if the actual stored value is 0? It’ll reload from the database every time. Enter the Nullable Type: Nullable Value Types (Visual Basic) Nullable Types (C#)

Private Shared _someSetting As Nullable(Of Integer)
Public Shared ReadOnly Property SomeSetting As Integer
    Get
        If Not _someSetting.HasValue Then
            _someSetting = GetSomethingFromTheDatabase()
        End If
        Return _someSetting.Value
    End Get
End Property

It’s just like a normal Integer, plus it can also be Nothing. Now we populate the variable only if it actually has not been set, then we return the Value of the Nullable Type.

You can also shortcut the declaration like this:

Private Shared _someSetting As Integer?

As the name implies, you can use the Nullable Type with any Value Type: Value Types and Reference Types.

System.DBNull.Value

System.DBNull.Value is really only useful for checking outputs from the database and setting NULL values into the parameters of a stored procedure. Because it’s primarily used when communicating directly with the database, that’s normally the only time you’ll need to compare a value to DBNull. I’m sure there’s other places it’s appropriate, but here’s some places it’s not appropriate:

DBNull cannot be converted to a String (not even with Option Strict Off):

Alt

Because of this, the following will always evaluate to False.

Debug.Print(myString Is System.DBNull.Value)

Also always False:

Debug.Print(Convert.IsDBNull(myString))

Is my file writable?

Trying to open the file inside a Try...Catch block is a bad idea.  Try...Catch structures are expensive.

Private Function FileIsNotLocked(ByVal fullPathToFile As String) As Boolean
        Dim blnReturn As Boolean = True
        Dim fs As System.IO.FileStream

        Try
            fs = System.IO.File.OpenWrite(fullPathToFile)
            fs.Close()
        Catch ex As System.IO.IOException
            blnReturn = False
        End Try

        Return blnReturn
    End Function

Instead, ask the file system directly. (Requires a ‘System.Security’ import.)

Private Function FileIsWritable(filePath As String) As Boolean
        If Not File.Exists(filePath) Then
            Return False
        End If
        Dim permissionSet = New PermissionSet(Permissions.PermissionState.None)
        Dim writePermission = New Permissions.FileIOPermission(Permissions.FileIOPermissionAccess.Write, filePath)
        permissionSet.AddPermission(writePermission)
        Return (permissionSet.IsSubsetOf(AppDomain.CurrentDomain.PermissionSet))
    End Function

Update 3/13/16: Try...Catch blocks aren’t nearly as expensive now as they were back in .NET 1.0, but it’s still a good idea to proactively interrogate the file for its security.