December 2008 Blog Posts

CaBiN fEvEr

The snow on the bush shows how deep it is - that bush is flat on top.
From Arctic Blast 2008

I am going out of my fucking mind with this snow. I hate snow.

Seriously. I've been stuck at home for like four days now and the snow just keeps getting deeper, indicating that I'm going to be stuck here until time ends, or at least until next weekend when it's supposed to get warmer and melt off a bit.

Yesterday ended with about 10" of snow and a quarter-inch sheet of ice on top of that, so you had to break through the ice if you wanted to walk around.

I tried to take the garbage out yesterday, but I can't because the snow has piled up so high I can't get the gate to the cans open. I've repurposed a recycling bin as a temporary garbage holding area until I can get to the cans. No, the irony of using a recycle bin as a garbage can is not lost on me.

The local news stations are calling this "Arctic Blast 2008." I guess they all get together and decide what to call various weather systems so the reporting is consistent. I guess "Arctic Blast" is as good a name as any.

I've been able to work from home, but Jenn's stuck at home and unable to work, which means I'm in here sitting at the kitchen table, destroying my back in these crappy chairs, trying to do tasks that require a lot of in-person collaboration without actually getting together in person with people, and she's in the other room playing Lego Batman. I wanna play Lego Batman and not work. There are so many other things I can imagine doing at home that don't involve work. I have months of comics backed up that I could read. Movies to watch. Games to play. Cats to pet. (OK, I admit I pet the cats while I'm working at home.)

Anyway, working at home is a motivational challenge. I'd much rather be in the office since then I could get my collaborative work done and not be distracted by, say, a kitten jumping out of the Christmas tree like the squirrel in Christmas Vacation.

Speaking of Christmas Vacation, Jenn and I have been watching holiday movies the last few days and I have to say, I think that and A Christmas Story are possibly the best holiday movies ever made. Ever. In fact, here's my top five list:

  1. Christmas Vacation
  2. A Christmas Story
  3. Die Hard
  4. Elf
  5. Emmet Otter's Jug-Band Christmas

You'll notice there aren't any of the old "classics" on there like Miracle on 34th Street or It's a Wonderful Life. I'm just not into them. I mean, I'll watch, but for various reasons, watching Die Hard (the original) is what really says, "It's Christmas!" to me.

The house is somewhat decorated for Christmas. Our new kittens, Kai and Stan, are pretty destructive little beasts, so we've been slowly introducing things to them in an effort to "warm them up" so they don't just go apeshit and destroy everything. We figured that we couldn't hang the stockings from the fireplace because the hangers were only really made to support the weight of the sock and a few small gifts, not the weight of a kitten, so we've got them hung by the stairs. We had to use nylon rope to hang them because they chewed through the ribbon we started out with and the string we subsequently replaced the ribbon with. We have a fake tree and the bottom row of branches is totally bent down and looks horrible because the kittens "ride" the branches and attack them. (You wouldn't think they could break off metal branches, but they did break a piece of one off.) Jenn put gifts under the tree and the kitties have been helping by chewing the living crap out of the bows. I'm surprised the packages are still wrapped.

Kai and Stan learn about snow.
From Arctic Blast 2008

We introduced the kittens to snow, which was pretty funny. They're indoor kittens so they really don't get to experience some of that stuff first-hand. We filled up a Tupperware bowl full of snow and brought it in. They dig around in it, pull some out onto the floor, shake their paws (it's cold!), and then smack the snow across the floor. They'll watch it go, then dig some more out and the process continues. They pretty close to emptied that bowl of snow out onto the floor.

Let's see, what else has gone on during the snow?

Our Scooba broke. I think the pump in it went bad. It just puts this blue light on, the equivalent of your car's "Check Engine" light. After jumping through a few support hoops, it turns out Scooba is no more. Lame. Now I need to decide if we want to replace it or not. They have an out-of-warranty replacement program that gets you a new robot for a steep discount and I'll probably be participating in that. I really like the Scooba and it does a pretty decent job. In the meantime, I guess we'll mop the old fashioned way.

The movies we've been watching have mainly been through my media server, just proving the functionality, which is really cool. Having access to the movies is really nice and super convenient.

Cookies, cookies everywhere!
From Arctic Blast 2008

Jenn's been making a lot of cookies and baked goods while we're stuck at home. A lot of cookies. They're freaking everywhere. Counters, cupboards, fridge...

Needless to say, I'm probably not eating as healthy as I should be. I had a rum ball for breakfast this morning as Jenn was yelling in from the other room, "That's not a breakfast food!" Hmmm. I guess it is now.

That and egg nog, some of which my cat is being very persistent about trying to get.

Oh, here's an entirely unrelated story but something I was going to blog anyway. We had a cat pooping on the floor next to the litter box. Not sure why, but it'd always be right next to the box. Pee in the box, poop outside the box. Couldn't figure out why.

Talked to the vet, and there are two reasons a cat will do something like that: medical or behavioral. Since the poop was solid, it wasn't medical, so we figured it was behavioral.

Behavioral reasons include the cat not feeling safe in the box, the cat not liking the litter, the box being inconveniently located, and so on. So, to address all of these things, we added a second box in a different corner of the room and made sure there was no top on either box so no one would feel "pinned in" or anything - your choice of location, easily escapable, for your pooping pleasure. We also put a Feliway plug-in there in case the kitties were feeling nervous. (Feliway is a cat pheromone analog that calms cats down. Humans can't smell it.) None of this stopped the issue. You know what it was?

Box not big enough.

We had two pretty big boxes (the large size at the store) but our new kittens are serious diggers - like little steam shovels. The vet recommended we get a bigger box, just to see. We ended up getting this thing that resembles a gigantic Rubbermaid storage container. Takes 25 pounds of litter to fill. But you know what?

No more poop on the floor.

So there you go.

Anyway, that's about it going on around here. Cabin fever has set in, and we're waiting for the snow to go.

Here's wishing you and yours peace and love for the holidays.

Kai and Xev take a nap.

HP Photosmart D7260 - Great Printer

A few months ago I was looking for a replacement printer and scanner. I tried out a couple of all-in-one solutions (Canon PIXMA MX850 and HP C7280) and was consistently disappointed in the scan quality so returned both of them and decided to get a separate printer and scanner.

HP Photosmart D7260For the printer, I ended up getting an HP Photosmart D7260.

The quality on it is amazing, just like the C7280 was, but for $70 cheaper and with a better LCD screen on it. The D7260 can connect directly to your network over ethernet, which is how I have it hooked up, and it lets me sit in the living room with the laptop and print wirelessly over the network - nice. There are slots for several different memory card types right on the front, too, so you can take the card out of your camera, plug it right into the printer, and print directly from there - no need to load the photos on your computer first.

Something else I noticed just the other day that was kind of cool: My PS3 has a "printer settings" section that I never looked at. I went in there, told it to search for printers, and it was able to locate and connect to the D7260 over the network with no issues. I'm not sure what I'll be printing from my PS3, but the neat factor is definitely there.

I've only had two problems with it, both of which are minor:

First, for a while I was having troubles getting the paper to feed and thought the rollers might be messed up. Turns out the paper was sort of bunched up and wouldn't easily separate. Putting different paper in fixed that.

Second, this thing is loud. I'm not talking like "Xbox 360 cooling fan" loud, either. This is like "one wood chipper being fed through another wood chipper" kind of loud. Whenever you print, it wakes up and goes through some initialization gyrations, which sound like metal grinding metal. The print happens, which is not too loud, and then when it's done it goes through something that sounds just like the initialization actions. If you can ignore the noise, this thing is pretty cool.

Dancing Muppet Slinkies

We were talking today about Sesame Street and the Muppets and some of the stuff that we got traumatized with as children watching TV in the US and I remembered the dancing slinkies - one of my favorite Muppet skits next to classic Mahna Mahna.

Storing Configuration Settings Behind a WCF Service

One of the challenges I'm facing in the project I'm working on is that we want to store configuration values for the system in a central location that can be accessed via a service... but that's not how most .NET developers are used to working with configuration. Most folks are used to ConfigurationManager or WebConfigurationManager, something like this:

NameValueCollection config = (NameValueCollection)WebConfigurationManager.GetSection("mySection");
this.SomeObject.Value = config["key"];

That's great if everything is stored in your app.config or web.config, but if you've got everything behind a service, it's trickier. You've got to get a proxy to your service, get the approriate values, handle exceptions... it's a lot messier, and if you're trying to get a bunch of devs up to speed using that, it's going to take a bit. Wouldn't it be nice if they could just use the stuff they're used to?

There are a couple of ways that can happen.

First, you can use a ProtectedConfigurationProvider implementation. You might be used to seeing these in the form of things like the RsaProtectedConfigurationProvider that you'd use to store settings encrypted in your configuration files. The cool thing is, the ConfigurationManager doesn't really care what's stored in the <EncryptedData> element in your config. For example, when you use the RsaProtectedConfigurationProvider, you'll see something like this in your config file:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <mySection configProtectionProvider="RsaProtectedConfigurationProvider">
    <EncryptedData Type="http://www.w3.org/2001/04/xmlenc#Element"
      xmlns="http://www.w3.org/2001/04/xmlenc#">
      <EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#tripledes-cbc" />
      <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
        <EncryptedKey xmlns="http://www.w3.org/2001/04/xmlenc#">
          <EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-1_5" />
          <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
            <KeyName>Rsa Key</KeyName>
          </KeyInfo>
          <CipherData>
            <CipherValue>(encrypted data here)</CipherValue>
          </CipherData>
        </EncryptedKey>
      </KeyInfo>
      <CipherData>
        <CipherValue>(encrypted data here)</CipherValue>
      </CipherData>
    </EncryptedData>
  </mySection>
</configuration>

But everything inside <EncryptedData/> is entirely up to the configuration provider. Since you can define your own providers, what if you did something like this:

<?xml version="1.0"?>
<configuration>
  <configSections>
    <section name="mySection"
      type="System.Configuration.AppSettingsSection, System.Configuration, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"/>
  </configSections>
  <configProtectedData>
    <providers>
      <add name="ServiceConfigurationProvider"
        type="Framework.ServiceConfigurationProvider, Framework"
        endpointName="WSHttpBinding_IConfigurationService"/>
    </providers>
  </configProtectedData>
  <mySection configProtectionProvider="ServiceConfigurationProvider">
    <EncryptedData>
      <values>
        <value key="value1"/>
        <value key="value2"/>
      </values>
    </EncryptedData>
  </mySection>
</configuration>

See what we have there?

  • A custom section that (for simplicity) is just a key/value section like AppSettings.
  • A custom protected config provider that has a special extra configuration property - an endpoint name (that would correspond to something in your <system.serviceModel> configuration).
  • A section that uses the configuration provider you specified... and notice how the contents of the <EncryptedData> are simply keys? These are the values you'd want to retrieve from your configuration service.

So how would you do it? You could implement a provider that looks like this:

using System;
using System.Configuration;
using System.Xml;
using System.Xml.Linq;
using System.Collections.Generic;
using System.Reflection;
using System.ServiceModel;
using System.Text;

namespace Framework
{
  public class ServiceConfigurationProvider : ProtectedConfigurationProvider
  {
    public string ServiceEndpointName { get; set; }

    public override XmlNode Decrypt(XmlNode encryptedNode)
    {
      List<string> keysToRetrieve = new List<string>();
      foreach (XmlNode value in encryptedNode.SelectNodes("/EncryptedData/values/value"))
      {
        XmlAttribute keyAttrib = value.Attributes["key"];
        if (keyAttrib != null && !String.IsNullOrEmpty(keyAttrib.Value))
        {
          string key = keyAttrib.Value;
          if (!keysToRetrieve.Contains(key))
          {
            keysToRetrieve.Add(key);
          }
        }
      }

      ChannelFactory<IConfigurationService> factory = new ChannelFactory<IConfigurationService>(this.ServiceEndpointName);
      IConfigurationService service = factory.CreateChannel();
      AppSettingsSection section = new AppSettingsSection();
      foreach (string key in keysToRetrieve)
      {
        section.Settings.Add(key, service.GetValue(key));
      }
      XmlDocument doc = new XmlDocument();
      StringBuilder builder = new StringBuilder();
      using (XmlWriter writer = XmlWriter.Create(builder))
      {
        typeof(AppSettingsSection)
        .GetMethod("SerializeToXmlElement", BindingFlags.Instance | BindingFlags.NonPublic)
        .Invoke(section, new object[] { writer, "root" });
      }
      doc.LoadXml(builder.ToString());
      return doc.DocumentElement;
    }

    public override XmlNode Encrypt(XmlNode node)
    {
      throw new NotImplementedException();
    }

    public override void Initialize(string name, System.Collections.Specialized.NameValueCollection config)
    {
      this.ServiceEndpointName = config["endpointName"];
      base.Initialize(name, config);
    }
  }
}

Note that, in the above, there's a lot I'm not doing to keep it simple - I'm not properly closing the service channel, I'm not handling errors in the call, etc. It's for illustration purposes. I'm also doing some reflection magic to get the AppSettingsSection to serialize to XML so I don't have to manually do it. Wouldn't that be nice if it was public?

The point is, you can use the protected configuration provider mechanism to store things elsewhere - service, database, etc. Consuming something like this would look exactly like the previous example. You'd literally never know the difference as a consumer of the settings. The problem with this solution is that once the value is read, it's cached and never re-read. Which is to say, the service will only ever get called once. If the configuration value changes in whatever data store the service is wrapping, you'll never get it in your app. Also, if you wanted strong typing, you'd have to implement a custom configuration section that handles strong typing and that's what your provider would return from the Decrypt method. (Like I said, a simple AppSettingsSection keeps it simple.)

A solution that's chattier but overcomes this caching issue is to implement a special ConfigurationSection. In that section, you can simply have a method that wraps the service call.

Your configuration file might look like this:

<?xml version="1.0"?>
<configuration>
  <configSections>
    <section name="mySection" type="Framework.ServiceConfigurationSection, Framework"/>
  </configSections>
  <mySection endpointName="WSHttpBinding_IConfigurationService"/>
</configuration>

It's a little shorter, but you can still see there's a service endpoint name in there that'd correspond to the configuration service you'll be calling. You can also see that we're using a custom configuration section rather than one of the standard out-of-the-box ones.

The ConfigurationSection implementation might look like this:

using System;
using System.Configuration;
using System.ServiceModel;

namespace Framework
{
  public class ServiceConfigurationSection : ConfigurationSection
  {
    [ConfigurationProperty("endpointName", IsRequired = true)]
    public string ServiceEndpointName
    {
      get
      {
        return (string)this["endpointName"];
      }
      set
      {
        this["endpointName"] = value;
      }
    }

    public string GetValue(string key)
    {
      ChannelFactory<IConfigurationService> factory =
        new ChannelFactory<IConfigurationService>(this.ServiceEndpointName);
      IConfigurationService service = factory.CreateChannel();
      return service.GetValue(key);
    }
  }
}

Again, there's a lot I'm not doing in there to keep it simple, but, again, you see the idea - the GetValue method on the section wraps the service call. Consuming this looks very similar to the original example:

ServiceConfigurationSection config = (ServiceConfigurationSection)WebConfigurationManager.GetSection("mySection");
this.SomeObject.Value = config.GetValue("key");

This version retrieves the value every time you ask for it, which gets you around the caching issue. That said, the developer using this mechanism should probably be made aware of what's going on so he or she doesn't wonder why performance has gone down the tubes on that page that uses 150 bajillion configuration values.

Coach Hines

Coach Hines is a character on MadTV that teaches at a high school for boys. He's one of my favorite characters on the show. Anyway, I was working on some fairly convoluted process stuff the other day and it reminded me of the choreography from his version of Oliver Twist:

First 100% on Drums!

I blogged a while ago (in my drum comparison entry) that I'd never gotten 100% on a song playing the drums in Rock Band, even on easy difficulty, and I generally play on medium or hard. It was like my own personal Curse of the Bambino going on - no matter what I did, I'd always miss one note. Maybe it was a stupid mistake on my part, maybe it was a double-hit registered, but whatever the reason, never did I get 100%.

The curse has been broken!

100% on Hella Good

Last night I got 100% on the No Doubt song "Hella Good" on medium difficulty. Interestingly enough, I actually got two 100% songs last night - the other was "My Best Friend's Girl" by The Cars. The hardest songs in the world? Not by a long shot, but I'm still pretty proud. I'm not afraid to admit I was singing songs about how I'm the King of Drums last night. No, I'm not an awesome drummer, but I was pretty stoked with myself.

And, yes, you'll see my character's name is "Alyss." All of my characters are named based on Frank Beddor's Looking Glass Wars characters. My guitarist is Hatter Madigan, my band is Wonderland's Millinery... you get the idea. Here's Alyss:

Alyss from Wonderland's Millinery

Black Uniball Roller Micro 0.5mm

Best. Pens. Ever.

Black Uniball Roller Micro 0.5mm

Just went out at lunch and picked myself up a box of these. Life's too short to write with crappy pens.

Why I Don't Make Weeknight Plans

I generally don't make plans to hang out with people during the week. It's not that I don't like visiting friends, it's just that things never seem to quite work out. Folks are rushed getting off work, usually have to take care of some things at home, and you don't really want to be out too terribly late on a "school night" because you do have to make it to work the next day.

Of course, sometimes I forget and we try. Last night was one of these times.

Earlier this week we got called by the local comedy club offering tickets to the Thursday night show. It sounded like fun, and we hadn't been out with friends for a while, so we accepted. The show started at 8pm Thursday, but you had to pick up your tickets between 6pm and 7:15pm. No problem, right?

The first issue we ran into was that almost everyone else has adopted the same no-weeknight-events policy we have, so of the 8+ people we invited, two were able to make it. That's OK, though, because they're really good friends and fun to hang out with, so we weren't really sweating the fact it wasn't as big as we hoped. (It turns out it's probably a good thing it was small.) As long as we could pick one of them up at home on the way, the other would meet us at the club and all would be well.

The second issue was that Jenn had a chiropractor appointment at 6pm on Thursday. "Don't worry," she said, "the chiropractor is really fast and if I'm there early, he always takes me early. I'll just get there early." I wasn't terribly convinced, but hey, if she says she can make it, I'll believe it.

Here's how it turned out:

Jenn got out of work a half hour later than she usually does because it got busy. She noticed that her driver's side headlight might be out and sent me a text message as such. No big deal. Anyway, getting out late put her behind a bit so she couldn't quite get to the chiropractor as early as she hoped.

The chiropractor had a backlog of people hanging out in the lobby when Jenn arrived, so there was no way she was going to get taken care of early, and may be a few minutes late. It was at this point I was getting worried because I started doing the math:

  • 10 minutes for Jenn to come home from the chiropractor and pick me up.
  • 10 minutes to go pick up one of our friends.
  • 20 minutes to make it downtown in medium traffic.
  • 5 minutes to park if we're lucky.

So as long as Jenn could leave by 6:30p, we could still barely make it in time to get the tickets. Close, but not a big problem. Plus, since I was sitting at home waiting, it'd give me more Fallout 3 time. I sent a message to our friend to let her know we'd pick her up a little later than we expected, but we'd still make it.

About 6:15p Jenn calls to say she's locked herself out of the car. Remember that text message earlier about the driver's side headlight? She turned the car battery on (power on, but not the engine), which locks the car doors, then got out of the car to see if the headlight was out. While she was out "knocking on the headlight" (no, I don't know what the knocking was for), the car door shut. Locked out. Now I've got to bring her the spare keys.

We're a little behind where I was hoping to be, but we're still looking OK.

  • 5 minutes to get my shoes on, find the spare key, and leave.
  • 10 minutes for me to take Jenn the spare key and unlock the car.
  • 10 minutes to go get our friend and leave my car at their house.
  • 20 minutes to make it downtown in medium traffic.
  • 5 minutes to park if we're lucky.

It's about 6:15p, and with the 50 minutes it's going to take to get things together, we have a 10 minute window (between 7:05p and 7:15p) where we can still pick up the tickets. Tight.

I make it to Jenn in the expected amount of time, unlock the car, and we both hop in and get ready to go. I say "get ready" because Jenn's battery is dead and she needs a jumpstart. Now I know we're not going to make it.

  • 15 minutes to push Jenn's car to a spot where I can get to it, move my car, give the jump start, and put everything away.
  • 10 minutes to go get our friend and leave my car at their house.
  • 20 minutes to make it downtown in medium traffic.
  • 5 minutes to park if we're lucky.

It's about 6:30p and we won't make it to the ticket booth until 7:20p, and that's if everything goes right.

"Hey, Jenn, should we just call this off?"

"No, we can make it."

No, we can't. I love Jenn to death, but she doesn't listen to me in situations like this, and she knows it, so it's best just to go with it, as frustrating as it is. "OK, well, while I'm getting my car moved and getting the jump start going, let our friend know that we're going to be even later."

"I don't have her phone number."

Now I'm I/O-bound - I'm the only one that can get the cars set up for the jump start, and I'm the only one that can coordinate this whole thing. Does a text message or a call really take that long? No, but when minutes are literally counting, it adds up.

I got Jenn started, we put things away, and we headed off to our friend's house. I, of course, hit literally every stupid stop light on the way there, so a 10 minute trip took closer to 15 minutes. (Thanks, City of Hillsboro, for having every light be pressure-sensitive instead of timed.) I called Jenn about halfway there to let her know this wasn't going to happen. By the time I got there, Jenn had already told our friend and we decided to make the best of it and at least get some dinner.

Dinner was good, and it was nice to see our friends, so it all worked out for the most part. Makes me wonder if we shouldn't have just gone for the smaller plans - just dinner - to begin with. Maybe next time. If there's a next time. Maybe we should just wait until the weekend like we always do so it won't go so horribly wrong.

Amazon Universal Wish List Button

A while ago, I'm not sure when, Amazon released a "Universal Wish List Button" feature that allows you to add a bookmarklet to your browser and when you see something you like, even on another web site, you can click the bookmarklet and have it added to your Amazon wish list.

I find this feature exceptionally handy, particularly around the holiday season when people ask what I want.

Nicole Kidman - Added via the Universal Wish List Button.

Somehow, I don't think Santa's going to be able to help me out with this one. :)

The Problem With "Release Early, Release Often"

One of the mantras of agile software development is "Release Early, Release Often" - every time you have a new, working version with fixes and updates, you should put that in the hands of the consumer. It's a great idea - if there's something new the consumer of your product, be they developers or otherwise, could use, make it available.

The problem lies in the reciprocal expectation of the producers of said product that the consumers will instantly be able to take the latest available version. This isn't always a valid expectation and can get in the way of product support.

The happy-path scenario is something like a web application that is deployed in a central location and is consumed by various users. A new version of the web app becomes available, the service host deploys the new version, and the end users immediately have the new features and fixes available to them. When the customers need support, it's pretty safe to assume they're on the latest version.

What happens if the product isn't like that, though? What if it's a framework component like a logging library? In my experience, there's a little more work required as a consumer of a third-party framework component to take the latest version than just "download and go." It might look that way to the folks providing that component, but in larger environments that take third-party components on as dependencies, for every new version that comes out you have to consider:

  • Has the licensing changed? (If so, do we need to run this by Legal to get approval for the upgrade?)
  • Is there any fee associated with taking the upgrade?
  • What are the breaking changes?
  • What got fixed?
  • Were we inadvertently assuming incorrect behavior that has changed?
  • Were we working around incorrect behavior that's now rectified?
  • For .NET dependencies, if it's strongly-named and not installed into the GAC, do we need to add binding redirects to configuration? If so, where?
  • Does the product need to be installed on each developer machine or is it a dependency that can be checked in to the central source code repository and seamlessly updated?
  • If we have to support developers working on different versions of our product at the same time and each of our product versions relies on different versions of the dependencies, how does this change the manner in which developers switch their envrionments from version to version?

...and so on. Just because a new version is out doesn't mean your customer can take it. I, as a customer, have to budget time in the schedule for investigating all of the above, testing the upgrade on a standalone developer system, performing any code changes required to take the upgrade, and synchronizing any updates to the developer environments.

So "release early, release often" doesn't help me much in this scenario, and contacting your support department to ask questions about version 1.2 and you not helping me because the first line of support is to "update to version 1.3 and see if the issue is fixed" is crap. (I understand this line more from open-source/freeware projects than I do if I paid a licensing fee and expect support.)

My message to the "Release Early, Release Often" folks: Remember who your end users are. It's great that you're getting the latest version out as often as possible, but it may not be feasible for your customers to take what you're dishing out as soon as it's available.

Easier Subtext 1.9.5b Database Maintenance

A few months back I was working on my blog and did some database maintenance to help slim down the size of things with respect to referral logs based on some logic posted by Phil Haack. Just a couple of days ago, I wanted to see how things were looking in there and noticed the database was starting to get a little big again.

The problem with doing database maintenance, at least for me, is that I don't have a dedicated SQL instance and I don't have administrative rights, so I can't, for example, run a database backup to truncate the transaction logs, and if I mess things up I'm at the mercy of the operator on duty to eventually get to my help desk request and restore me. I also have to have them open up their firewall on a per-IP-address basis so I can connect with SQL Management Studio, and then I'm still sort of stuck because I can only connect from home - the firewall at work blocks that port, so I can't fix anything on the fly during the day. Normally this isn't a big issue, and the folks at my host are really good and pretty responsive, but it does limit my abilities.

What this all boils down to is that I need an administrative interface to do this sort of maintenance that's part of the application. So that's what I wrote.

Download the zip file, then drop the enclosed ASPX page in your Subtext "Admin" folder. It's an administration page so you do have to be logged in as an admin to use it. It doesn't add any navigation links to the admin site, so you do need to manually enter the URL to the page to get to it, but once you do, this page allows you to:

  • Clear the error log. Yes, you can do this from the error log page, too, but it's nice to have all of this in a central location.
  • See how many referrals you have in your database vs. how many of those are from search engines. The page lists out what qualifies as a search engine or spam referral so you'll know what this means. It's basically just a list of expressions that the page tries to match the URL against - nothing fancy.
  • Remove search engine referrals from the referral log. Qualifying spam referrals are also removed.
  • Reindex the referrals table and shrink the database. Do that after you clear out the garbage referrals.
  • See some size statistics on your database.
  • See the SQL script that the page will execute (in the event you'd rather run it yourself or are just curious).

And, of course, since all the code is right in the ASPX markup, you can adjust it as you see fit.

I have only tested this against Subtext 1.9.5b, since that's the version I'm running on. (I can't upgrade to Subtext 2.0 yet due to the medium trust problems.) And, of course, standard disclaimers apply: Use at your own risk, YMMV, I'm not responsible for if this truncates every table in your database and kicks your mom, etc.

[Download SubtextDatabaseMaintenance195.zip]

Upgrading Your Xbox 360 Hard Drive

I've been considering upgrading my hard drive from my 20GB drive to something larger in the potentially near future because I'm starting to get somewhat low on free space and, while I have a spare 20GB drive, switching drives isn't teh hawesome.

I was worried about the licensing issues - like whether or not the licenses would transfer from the old drive to the new one, since the DRM on Xbox Live has been the bane of my very existence - but my friend Alex has done the transfer process and it sounds like it came off without a hitch. I asked and he verified that he was able to log in and use content on the console from an account other than the one that purchased the content, which proves that the content was still licensed to the console, not just his account.

Maybe an upgrade will be in order soon.

Forcing ASP.NET 3.5 on IIS6

I realize I'm probably a bit behind the times on this one, but there was just something I wasn't getting until now.

I know that .NET 3.0 and 3.5 are still on the .NET 2.0 CLR. I get that. Today, though, I was writing a little web form - a one-page, all-code-in-the-ASPX web form to just do a quick test of something. I wasn't using Visual Studio, I wasn't creating a whole web project, I just wanted to drop a single ASPX file into C:\Inetpub\wwwroot and run it to see the outcome of a particular expression evaluation. Sort of like Snippet Compiler gone ASPX.

For the life of me, though, I could not get my .NET 3.5 LINQ code to compile. I had the correct <%@ Assembly %> directive to reference System.Core, I had everything looking right... but it wouldn't use the .NET 3.5 C# compiler, so it wasn't understanding my LINQ syntax.

The answer: You absolutely must specify the .NET 3.5 compiler in your <system.codedom> section of web.config. No choice. You can't just drop an ASPX file in there and expect the latest compiler to be used, even if you have it installed. Here's a snippet:

<system.codedom>
  <compilers>
    <compiler language="c#;cs;csharp" extension=".cs" warningLevel="4" type="Microsoft.CSharp.CSharpCodeProvider, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
      <providerOption name="CompilerVersion" value="v3.5"/>
      <providerOption name="WarnAsError" value="false"/>
    </compiler>
  </compilers>
</system.codedom>

.NET 3.0 and 3.5 did some great stuff to help you in using the features in ASP.NET. For example, the default web_mediumtrust.config (Medium Trust in ASP.NET) was updated to allow ReflectionPermission so you can use the LinqDataSource control without having to create your own custom trust level.

How come they didn't update the compiler for ASP.NET to be the latest? Instead, you have to put this in every web.config file of every application you make. (I didn't notice it because it's one of the things Visual Studio does for you when you create a new web project.)

Hanselman has a more complete article on the issue, but at a minimum, you need to update the compiler in your web.config.