Kevin Lewis Blog
All about CODE - VB.net, ASP.net, SQL Server, and everything in between

Computer Forensics

May 30, 2008 Posted by Kevin Lewis

I have a client which offers data recovery and computer forensics services. I am very happy to say that I have not been in need of their service (knock on wood).

We recently ran into a very strange problem with their site dropping completely out of google’s index. My first instinct was that the seo company they were previously working with had gone a little to far, and was responsible for getting them banned. However, upon further investigation, I learned that their site had vanished because of a problem with their robots.txt file. Apparently there was a problem with their custom 404 page which was raising a 500 server error. Google interpreted this differently than if their robots.txt simply didn’t exist at all. Instead google assumed that it must not be safe to crawl this site period, since they are unable to adhere to the robots rules.

The weirdest part of all, is from what I can tell, nothing has been touched on this site in years, and their 404 page has probably always behaved this way. Google must have recently changed something on their end which cause this unfortunate series of events.

The good news is, it only took me a couple minutes to fix the offending robots file and google had reindexed the site and was bringing in traffic again within a few days.

Sod

May 23, 2008 Posted by Kevin Lewis

I just wanted to give a shout out to one of my clients. They are probably the most “green” company I have ever worked with. We recently developed a custom accounting system for them. This reduced their paper usage by over 60%. They are no longer faxing/mailing reports between their satellite offices and the corporate office, because they now have access to that same data in real-time. And they can email statements directly to their customers.

We recently integrated their website into the accounting system. This allows users to order sod online, and eliminates redundant data entry.

On top of all that, their product keeps our air fresh and clean. They grow and distribute tons of grass every day.

Next hurdle, get them to the top for Sod Riverside, Sod Los Angeles,
Sod Sacramento

Remote Desktops MMC with alternate RDP port

May 22, 2008 Posted by Kevin Lewis

If you manage windows servers, or access remote desktops you should be using the Remote Desktops mmc plugin that comes in the Administration Tools Pack. The standard remote desktop application that comes with Windows XP is OK, but this one makes it much easier to manage multiple machines, and to save all your connection settings.

However, we did run into a major problem with the Remote Desktops MMC plugin. The initial copy of the Admin Tools did not allow you to specify an alternate port when entering the IP address of the server. It was really annoying and forced us to use the standard RDP client when connecting to servers with a non standard RDP port. Port 3389 is the default port, but for security reasons, it is a good idea to change the port your server listens on.

The Service Pack 2 edition of the Administration Tools finally fixed this bug, and now we can use the MMC plugin to manage all of our servers, regardless of the RDP port. Simply specify the port after the IP/name in the format 192.168.0.1:1234

LINQ to UPS to Calculate Shipping Rates

 Posted by Kevin Lewis

I decided to cleanup my old UPS Webservice Rate Calculation code, and implement some XML literals as well as Linq to XML. The below code is much easier to understand and maintain. I love the new xml syntax for accessing elements.


Private Function GetShippingMethodsFromWeb(ByVal ZipTo As String, ByVal Weight As Integer) As List(Of Linq.ShippingMethod)

    Const URL As String = "https://www.ups.com/ups.app/xml/Rate"

    If Weight < 1 Then Weight = 1

    Dim XMLAuth = <?xml version="1.0"?>
                  <AccessRequest xml:lang="en-US">
                      <AccessLicenseNumber><%= App.Settings.UPSAccessKey %></AccessLicenseNumber>
                      <UserId><%= App.Settings.UPSUserID %></UserId>
                      <Password><%= App.Settings.UPSPassword %></Password>

                  </AccessRequest>

    Dim XMLRequest = <?xml version="1.0"?>
                     <RatingServiceSelectionRequest xml:lang="en-US">
                         <Request>
                             <TransactionReference>
                                 <CustomerContext>Rating and Service</CustomerContext>
                                 <XpciVersion>1.0001</XpciVersion>
                             </TransactionReference>
                             <RequestAction>Rate</RequestAction>
                             <RequestOption>shop</RequestOption>
                         </Request>
                         <PickupType>
                             <Code>01</Code>
                         </PickupType>
                         <Shipment>
                             <Shipper>
                                 <Address>
                                     <PostalCode><%= App.Settings.ShipFromZipCode %></PostalCode>
                                 </Address>
                             </Shipper>
                             <ShipTo>
                                 <Address>
                                     <PostalCode><%= ZipTo %></PostalCode>
                                     <ResidentialAddressIndicator>0</ResidentialAddressIndicator>
                                     <CountryCode>US</CountryCode>
                                 </Address>
                             </ShipTo>
                             <Package>
                                 <PackagingType>
                                     <Code>02</Code>
                                     <Description>Package</Description>
                                 </PackagingType>
                                 <Description>Rate Shopping</Description>

                                 <PackageWeight>
                                     <Weight><%= Weight %></Weight>
                                 </PackageWeight>
                             </Package>
                         </Shipment>
                     </RatingServiceSelectionRequest>

    '<% If Length > 0 And Width > 0 And Depth > 0 Then %>
    '   <Dimensions>
    '    If Length Then
    '        sXML &= "<Length>" & Length </Length>"
    '    End If

    '    If Width Then
    '        sXML &= "<Width>" & Width </Width>"
    '    End If

    '    If Depth Then
    '        sXML &= "<Height>" & Depth </Height>"
    '    End If
    '    sXML &= "</Dimensions>"
    'End If

    Dim objRequest = System.Net.WebRequest.Create(URL)
    objRequest.Method = "POST"

    objRequest.ContentType = "application/x-www-form-urlencoded"

    Using myWriter = New System.IO.StreamWriter(objRequest.GetRequestStream())

        myWriter.Write(XMLAuth.Declaration)
        myWriter.Write(XMLAuth)
        myWriter.Write(XMLRequest.Declaration)
        myWriter.Write(XMLRequest)

        myWriter.Flush()
        myWriter.Close()
    End Using

    objRequest.Timeout = 5000

    Dim xmlResponse As System.Xml.Linq.XDocument

    Using objResponse = objRequest.GetResponse

        Using sr As New System.IO.StreamReader(objResponse.GetResponseStream())

            xmlResponse = System.Xml.Linq.XDocument.Load(sr)

        End Using
    End Using

    If xmlResponse...<ResponseStatusCode>.Value = "1" Then

        Dim Rates = From r In xmlResponse...<RatedShipment> _
                    Select Code = r...<Code>.Value, _
                    Charge = CDec(r...<MonetaryValue>.Value)

        Dim db As New DataContext

        Dim ShippingMethods = (From s In db.ShippingMethods _
                              Where s.Active AndAlso s.Carrier = "UPS" _
                              Order By s.SortOrder).ToList

        For Each s In ShippingMethods

            Dim r = Rates.SingleOrDefault(Function(a) a.Code = s.Code)

            If r IsNot Nothing Then
                s.ShippingCharge = r.Charge
            End If
        Next

        ShippingMethods.RemoveAll(Function(a) a.ShippingCharge = 0)

        Return ShippingMethods

    Else

        Throw New Exception("An error occurred while calculating UPS shipping rates: " & xmlResponse...<ErrorDescription>.Value & ". Location: " & xmlResponse...<ErrorLocationElementName>.Value)

    End If

End Function

Track Billable Hours

May 15, 2008 Posted by Kevin Lewis

What were you doing this day (May 15th) in the year 2000?

I spent 6 hours working on a redesign of PowerComEnergy.com, 0.4 hours working on LubeSource.com, and 0.3 hours fixing bugs on BestBolts.com.

You may be wondering how I know this. And yes, I do have a really good memory – but not that good.

You see, earlier that same month I also launched a site I built: TrackMyHours.com. I have a complete record of all my hours on any of my projects all the way back to 5/5/2000. TrackMyHours.com is great for consultants like myself who need to track billable hours.

Why I dumped Visual SourceSafe

April 19, 2008 Posted by Kevin Lewis

As a small software development shop, we have always used VSS. At first we had VPN issues so we jumped through all of the hoops to get it running on IIS with their provided webservice, But then it was always a pain getting a new project added to VSS properly (anyone else have projects like $/MyProject/MyProject - isn’t that annoying). And then whenever we had to get a new developer working on a project it was a difficult process to add a windows account, then a VSS account, then give them instructions on how to connect VSS using https and most of the time one little setting was off and they could not get connected.

On top of all of that, we could not view version history from within Visual Studio (the vss internet plugin does not support it). And when offsite, we could not even connect to sourcesafe using the regular client.

Checkin/Checkout kills productivity. I cannot tell you how many times I used to sit and wait on IM for one of my programmer to checkin the project file so I could add a form to our project. Or else wait for them to checkin a file (that they had not even finished testing) so I could make a small change to the file myself. But for years, I just thought that is how source control worked.

Then one day – my eyes were opened to concurrent development. I had heard of it before on open source projects, but didn’t really know anything about trunks, branches and merges. I knew visual studio team system worked this way, but every time I looked at the price tag on vsts, I knew that was not an option.

I read about CVS style source control and SourceGear Vault. Now my team and I have eliminated all of our connectivity issues, we are no longer waiting for checkins, and we can easily view file history (and even line history!). Vault has changed the way we develop and even the way we deploy websites and applications. Our deployment scripts grab the latest version right out of source control (we did this before with VSS, but their command line app did not work over http).

In Vault, setting up new projects is a breeze. I love the idea of creating a separate repository for each project (in vss we had to keep all our projects in one database so we could use http). And storing the repositories in SQL makes my backups much easier to maintain since they are already automated thru my existing SQL backup routine.

SQL Server Profiler

 Posted by Kevin Lewis

In my opinion, SQL Profiler is one of the least known and yet most powerful programmer tools out there. For those of you unfamiliar with Profiler, it basically lets you watch under the hood of your application to see all of the calls to your SQL database. It not only tells you each query or stored procedure call, it includes the read count, and duration, along with many other bits of helpful info.

I have given a handful of interviews for .net developers over the past few years and I always ask if they use SQL Profiler. There have been a couple of candidates that had heard of it but never really used it, and the only ones that had used it were dbas and not coders.

Today’s development tools make it super easy to have your database do a whole lot of unnecessary work. SQL Profiler the first app I open up when I am about to review or test one of my developers’ work. Sure enough, no matter how much I cram profiler down their throats it never fails. I can tell right away whether they had it open when they were testing their own code.

Here is what I find most:

  • LINQ queries that check for .Count > 0 or .Any() before binding (yes that’s one call to get the count and another to retrieve the results)
  • Binding controls multiple times, or even multiple Page.Databind calls
  • Frequent calls to “Settings” tables or lookup tables where the data rarely changes and should be cached
  • Calling the same function from within a loop instead of calling it once and setting a local variable
  • Poorly written LINQ queries that are executing much slower than they should
  • LINQ queries that are referenced or bound multiple times without being saved to a generic list
  • Tables that are missing indexes (high read counts/durations)

SQL Profiler is also extremely useful for debugging production applications since it can spy into a web.winforms app without affecting its execution. And when you are ready to optimize for performance, just filter for query durations over 500-1000 milliseconds.

Tips

  1. Uncheck all events except RPC:Completed and SQL:BatchCompleted
  2. Filter by Login name or Application name (set in the connection string) to filter out other activity.
  3. Filter for TextData Not Like exec sp_reset_connection.
  4. Save the Trace Template with the filters for that project so you do not have to set them again.