Archives / 2003 / December
  • Timezone Bug in Visual SourceSafe

    I checked in a file to Visual SourceSafe at 3:49pm.  My colleague applied a label to our files at 4:29pm.  Now wouldn't you think that my changes would be included in the label.  Well, you would be wrong if you think that VSS works in the obvious way.  I work in Atlanta using EST and my colleague was working in Mineapolis using CST.  Apparently VSS is totally clueless about timezones, and it does not use GMT internally.  Instead, it recorded my check-in at 3:49pm, and my colleague's label at 3:29pm, which was his local time.  That's right, even though my changes occurred before his label, his label was recorded before my changes!  I checked the contents of the labeled file just to make sure it was not a discrepancy of history being displayed, and it was not.  The labeled file clearly did not contain my changes that were checked in prior to the label.  Now I have to tell my team of consultants to always set their local PC time to EST before working with VSS!


  • Aspect-Oriented Programming (AOP) and Tracing

    I've been interested in Aspect-Oriented Programming (AOP) for some time now, but I hadn't actually experimented with it myself until recently.  That's not totally true, since most O/R mappers use AOP in their implementation, but I've only been a consumer of AOP in those cases.  For my own experiment, I decided to dig up an old MSDN article by Dharma Shukla, Simon Fell, and Chris Sells that used AOP for tracing.  By just decorating your class with an attribute, their code automatically adds tracing when you enter each method (with all your parameters included) and when you leave each method (with the return value included).  I modified it a little in my experiment to make explicit tracing calls much easier, and I also added a config switch to disable or enable the actual tracing output.  You could similarly modify it to output the trace to different “listeners”, or even add severity levels, but I'm just experimenting so I haven't done that yet.  This is all really cool stuff, especially since its so much easier to actually consume than other tracing methods like Log4Net, the Exception Management Application Block, or custom Trace Listeners.  By the way, that last one is an excellent article written by my good friend Jerry Dennany, who has an occassional clue here on these weblogs too.  Anyhow, my question to my few readers out there is how much does this context-bound call interception technique affect performance when you compare it to the other more traditional methods?  Also, are there any other things to worry about with these techniques, whether it be for tracing, persistence, security, or something entirely different?  And finally, what other uses have you dreamed up, or just heard about, for AOP, and what has been your experience using AOP for such things?


  • Cool Web Debugging Tools

    I don't usually repeat what others are saying, but these web debugging tools posted by Jon Galloway are awesome.  I've used ieHttpHeaders for quite a while, but the FullSource tool is new to me -- and its just incredible in what it does.  By the way, all web developers should at least have a fair idea of what's going on under the covers with the http protocol.  I used to send raw http commands using telnet, but ieHttpHeaders makes it trivial to see everything that is involved with a request.  Similarly, if you've ever used javascript to dynamically create your tables then you know its a pain to debug, and FullSource is what you've been needing.


  • Force ASP.NET Apps to Keep-Alive and Pre-Compile

    Larry Silverman has taken my Keep-Alive / Pre-Compile code for ASP.NET and extended it a fair amount.  I still get a lot of email about my version, so I thought I would share Larry's code for those interested.  Larry's version works with Forms Authentication, solving one of the problems I left undone.  It does require a super-user manually kick off the process, but that seems to be a fair tradeoff.  He's also separated the keep-alive functionality from the pre-compilation, and added a base page class for pre-compilation to avoid real functionality.  There's also an email sent about success or failure, along with the exceptions that occurred.  So if you liked my keep-alive and pre-compile code, but need more, then this may be what you were looking for.


  • Force ASP.NET Apps to Keep-Alive

    The following code from Larry Silverman is an extension of my Keep-Alive / Pre-Compile code:

    Note: There are some references to TAConfig which is just a small helper class for reading config file settings.

    Imports System
    Imports System.IO
    Imports System.Net
    Imports System.Web
    Imports System.Web.UI

    Namespace Wilson.WebCompile
        Public Class GlobalBase
            Inherits System.Web.HttpApplication

            Private Shared _needsCompile As Boolean = True
            Private Shared _applicationPath As String = ""
            Private Shared _physicalPath As String = ""
            Private Shared _applicationURL As String = ""

            Private Shared _thread As System.Threading.Thread = Nothing
            Private Shared _timer As System.Timers.Timer = Nothing

            ' TA Customization
            Private Shared _precompileUser As TAUser = Nothing
            Private Shared _lastResponseCookies As CookieCollection
            Private Shared _server As String
            Private Shared _precompileProblems As New ArrayList
            ' End TA Customizations

            Public Event Elapsed As EventHandler

            ' Override and Indicate Time in Minutes to Force the Keep-Alive
            Protected Overridable ReadOnly Property KeepAliveMinutes() As Integer
                    Return TAConfig.GlobalBaseKeepAliveCycle
                End Get
            End Property

            ' Override and Indicate Files to Skip with Semi-Colon Delimiter
            Protected Overridable ReadOnly Property SkipFiles() As String
                    Return ""
                End Get
            End Property

            ' Override and Indicate Folders to Skip with Semi-Colon Delimiter
            Protected Overridable ReadOnly Property SkipFolders() As String
                    Return "_vti_cnf;test"
                End Get
            End Property

            Public Overrides Sub Init()
                _applicationPath = HttpContext.Current.Request.ApplicationPath
                If Not _applicationPath.EndsWith("/") Then _applicationPath += "/"

                _server = HttpContext.Current.Request.ServerVariables("SERVER_NAME")
                Dim https As Boolean = (HttpContext.Current.Request.ServerVariables("HTTPS") <> "off")
                _applicationURL = CStr(IIf(https, CObj("https://"), CObj("http://"))) + _server + _applicationPath

                _physicalPath = HttpContext.Current.Request.PhysicalApplicationPath

                If TAConfig.DoPrecompileOnAppInit AndAlso GlobalBase._needsCompile Then
                    GlobalBase._needsCompile = False
                    _thread = New System.Threading.Thread(New System.Threading.ThreadStart(AddressOf CompileApp))
                End If

                If Me.KeepAliveMinutes > 0 Then
                    _timer = New System.Timers.Timer(60000 * Me.KeepAliveMinutes)
                    AddHandler _timer.Elapsed, AddressOf KeepAlive
                End If
            End Sub

            Private Sub KeepAlive(ByVal sender As Object, ByVal e As System.Timers.ElapsedEventArgs)
                _timer.Enabled = False
                ' Use the login page to keep the site alive.
                Dim url As String = _applicationURL & "public/login.aspx"
                Dim myWebResponse As WebResponse = Nothing
                    RaiseEvent Elapsed(Me, e)
                    myWebResponse = HttpWebRequest.Create(url).GetResponse()
                    _timer.Enabled = True
                    If Not myWebResponse Is Nothing Then myWebResponse.Close()
                    System.Diagnostics.Debug.WriteLine(url, "KeepAlive")
                End Try
            End Sub

            Private Sub CompileApp()
            End Sub

            Private Sub CommunicateProblems()
                If _precompileProblems.Count > 0 Then
                    Dim problem As String
                    Dim msg As New StringBuilder
                    msg.Append("The following pages threw unhandled exceptions during precompile.  They should be investigated." & vbCrLf)
                    For Each problem In _precompileProblems
                        msg.Append(problem & vbCrLf)
                    System.Web.Mail.SmtpMail.Send(TAConfig.SupportEmail, TAConfig.SupportEmail, "Precompile Problems", msg.ToString)
                End If
            End Sub

            Private Sub CompileFolder(ByVal Folder As String)
                Dim _file As String
                For Each _file In Directory.GetFiles(Folder, "*.as?x")

                Dim _folder As String
                For Each _folder In Directory.GetDirectories(Folder)
                    Dim skipFolder As Boolean = False
                    Dim item As String
                    For Each item In Me.SkipFolders.Split(";"c)
                        If item <> "" And _folder.ToUpper().EndsWith(item.ToUpper()) Then
                            skipFolder = True
                            Exit For
                        End If
                    If Not skipFolder Then
                    End If
            End Sub

            ' Lots of customizations in this method.
            ' We needed to add code to create Forms Auth cookies, and
            ' save the cookies returned from every request to be passed up with
            ' the next request.
            Private Sub CompileFile(ByVal File As String)
                Dim skipFile As Boolean = False
                Dim item As String
                For Each item In Me.SkipFiles.Split(";"c)
                    If item <> "" And File.ToUpper().EndsWith(item.ToUpper()) Then
                        skipFile = True
                        Exit For
                    End If

                If Not skipFile Then
                    Dim path As String = File.Remove(0, _physicalPath.Length)
                    If File.ToLower().EndsWith(".ascx") Then
                        Dim virtualPath As String = _applicationPath + path.Replace("\", "/")
                        System.Diagnostics.Debug.WriteLine(virtualPath, "Control")

                        Dim controlLoader As Page = New Page
                        Catch ex As Exception
                            ' Ignore failures in loading user controls.
                            ' Lots of them seem to fail.
                            ' Not sure if they all fail, and if they get compiled even if they fail.
                        End Try
                    ElseIf Not File.ToLower().EndsWith(".asax") Then
                        Dim url As String = _applicationURL + path.Replace("\", "/")
                        ' Append our precompile querystring so the page exits immediately.
                        url &= "?precompile=true"

                        System.Diagnostics.Debug.WriteLine(url, "Page")

                        Dim myWebResponse As WebResponse = Nothing
                        Dim myWebReq As HttpWebRequest
                            myWebReq = CType(HttpWebRequest.Create(url), HttpWebRequest)
                            myWebReq.AllowAutoRedirect = False
                            ' Add a new CookieContainer to the web request.
                            ' The default property is Nothing (i.e. no container), so we must do this.
                            myWebReq.CookieContainer = New CookieContainer

                            ' Our site requires Forms Authentication on most pages.
                            ' Here we set up a cookie with a valid Forms Auth ticket, and
                            ' attach it to the first request.
                            ' We're going to need to instantiate our default precompile super user, who has access to
                            ' view every page in the site.

                            If _precompileUser Is Nothing Then
                                ' Load the precompile user.
                                _precompileUser = New TAUser(TAUser.PRECOMPILE_USER_ID)

                                ' Initialize FormsAuthentication, for what it's worth

                                Dim ticket As New FormsAuthenticationTicket(1, _
                                    _precompileUser.Email, _
                                    DateTime.Now, _
                                    DateTime.Now.AddMinutes(TAConfig.SessionTimeout), _
                                    False, _
                                    _precompileUser.RoleId.ToString, _

                                ' Hash the cookie for transport
                                Dim hash As String = FormsAuthentication.Encrypt(ticket)

                                ' Create a System.Net.Cookie.
                                Dim myFormsAuthSystemNetCookie As New System.Net.Cookie(FormsAuthentication.FormsCookieName, hash, "/", _server)
                            End If

                            ' Attach the cookies we're hanging onto from previous requests.
                            If Not _lastResponseCookies Is Nothing AndAlso _lastResponseCookies.Count > 0 Then
                                Dim myCookie As System.Net.Cookie
                                Dim i As Integer
                                For i = 0 To _lastResponseCookies.Count - 1
                                    myCookie = _lastResponseCookies.Item(i)
                            End If

                            ' Send the request and get the response.
                            myWebResponse = myWebReq.GetResponse()

                            ' Here we save the cookies returned from the site for the next go-round,
                            ' if any are returned.
                            If Not myWebResponse Is Nothing Then
                                Dim myHttpWebResponse As HttpWebResponse = CType(myWebResponse, HttpWebResponse)
                                If myHttpWebResponse.Cookies.Count > 0 Then
                                    Dim myCookie As System.Net.Cookie

                                    If _lastResponseCookies Is Nothing Then
                                        _lastResponseCookies = New CookieCollection
                                    End If

                                    Dim i As Integer
                                    For i = 0 To myHttpWebResponse.Cookies.Count - 1
                                End If
                            End If
                        Catch ex As Exception
                            _precompileProblems.Add("Page: " & url & " " & ex.Message)
                            If Not myWebResponse Is Nothing Then myWebResponse.Close()
                        End Try

                    End If
                End If
            End Sub
        End Class
    End Namespace

    Note:  This is the base page that all pages should inherit from in order to have a pre-compile only type of action.

    Public Class PrecompilablePage
        Inherits Page

        Protected Overrides Sub OnInit(ByVal e As EventArgs)
            If Not Request.QueryString("precompile") Is Nothing Then
            End If
        End Sub
    End Class