Tuesday, February 17, 2009

Visual Studio installer projects - which project did I forget to include?

I made an interesting discovery while working on a project the other day. As I watched the compiler output from a particularly large solution I noticed several of the required assemblies were being built after the installer project. The reason was that someone forgot to add these assemblies to the installer. Normally this isn't hard to find and fix, but this particular solution has 150 projects, 149 of which need to be included in the installer. Manually comparing the list of installer dependencies to the list of projects takes quite a bit of time and is error-prone. Fortunately we can use the Project Dependencies screen to quickly find the missing items.

To illustrate, I've create a simple solution with five assembly projects and an installer project.


As I add each project to the installer, we see the primary output added within the installer


It looks simple enough when you only have a few projects. Add a couple dozen more and you can see that things are going to become more difficult.

To get a better view of the installer's dependencies, right-click on the solution in the Solution Explorer and choose "Properties." On the Property Pages dialog, choose Common Properties > Project Dependencies from the tree on the left. Under the Project dropdown on the right, select the installer project.

Here is my installer before adding any projects


After I add AssemblyA to the installer, the Project Dependencies looks like this


Notice the box beside AssemblyA now contains a checkbox. As I continue adding projects, each of these will be similarly checked. Below is an example after all but one assembly has been added. Even with 149 projects to include, it takes very little time to scan through the list and see which items remain unchecked.

Wednesday, February 11, 2009

Subversion failure to copy file

We ran into some difficulty today adding a new project to our build server. We kept getting an svn error stating:

"Can't copy 'C:\MyProject\_svn\tmp\text-base\web.config.svn-base' to 'C:\MyProject\_svn\tmp\web.config.tmp.tmp'.

Not the most helpful of error messages, as I have no knowledge of the inner-workings of svn. Fortunately, a quick Google search turned up a TortoiseSVN FAQ entry that proved useful.

In our code repository we had two web.config files in the same directory. The only difference was one had an initial uppercase 'W' while the second was lowercase. SVN handles this just fine internally. The problem occurs when you attempt to pull them down to the same location on a Windows machine. Since Windows ignores casing, these were being treated as the same file. SVN was complaining that the folder didn't contain the second copy of the file. Deleting one of the copies in SVN fixed the build error.

Friday, January 23, 2009

Row tests in NUnit

[Update 8/19/09: As of version 2.5, the extensions are no longer installed with NUnit. See my post on parameterized tests for a replacement.]

File this one under "old features I'm only now learning about." It seems NUnit has had a RowTest extension built in since March of 08...

For example, let's say I have a method called Add that adds two integers and returns the result. (The internals of that method are highly complex and I won't go into details here.) If you wanted to test two positive integers, a unit test would look like this:

[Test]
public void AddTwoPositiveNumbers()
{
    Assert.AreEqual(2 + 3, Add(2, 3), 
        "Add returned incorrect result");
}


To test two negative integers you would historically create a second, nearly identical unit test:

[Test]
public void AddTwoNegativeNumbers()
{
    Assert.AreEqual(-1 + -4, Add(-1, -4), 
        "Add returned incorrect result");
}


For reference, the two tests will appear under the class name when viewed through the NUnit GUI.



Now, these don't seem too bad, but what happens if you needs lots of nearly-identical tests? Or these tests need to perform additional steps? Copy/paste is a bad programming practice, even for unit tests. Fortunately, Nunit provides an extension (under the NUnit.Framework.Extensions namespace not surprisingly) that makes life much easier - the RowTest.

To use, create a single test method with parameters for the varying test data. Decorate the method with a RowTest attribute, and one Row attribute for each test case. The above unit tests can now be replaced by:

[RowTest]
[Row(2, 3)]
[Row(-1, -4)]
public void AddTwoNumbers(int x, int y)
{
    Assert.AreEqual(x + y, Add(x, y), 
        "Add returned incorrect result");
}


Changes to the test set becomes much easier, as does adding new tests. In the GUI, you'll see each Row split out as an individual test beneath the generic test:

Friday, December 12, 2008

Debugging XSL Transformations in VisualStudio 2005

I've been doing a bit of work lately with XSL transforms. While searching for a good transform debugging tool, I found that I had a decent one already installed - VisualStudio. To demonstrate, I pulled sample xml and xslt from the W3Schools site. (Click on each image to see a larger version.)


With the xsl template loaded in VS, right-click anywhere on the xslt and choose Properties. In the Properties window, set the Input value to point to the sample xml file (cdcatalog.xml in this case.)


At this point you can run the transform to view the output. Choose "XML" > "Debug XSLT" from the menu.


Nothing terribly interesting so far. The real fun doesn't start until you realize you can set breakpoints within the xslt. This is done the same as with other types of code files (by clicking the grey strip on the left or pressing F9.) Now, when you start the debugger, processing stops at the specified line. As expected, hovering the mouse over a variable or expression gives you a tooltip with the value.


Several of the other debug windows are also usable in this mode, such as the Locals and Immediate window.


Thursday, December 11, 2008

New Year's Resolutions

I was looking through my "to blog" list recently and I was reminded of something: I have a LOT of items on my list. Two or three times the number of posts I've written thus far. What's worse is some of these items would actually make an entire series of posts. At my current rate of posting (once or twice a month) I'm never going to finish.

Then there are the computer books sitting on my shelf, several of which I've not read much passed the intro. Some may not be worth digging into right now, but a few of them are "must reads." Code Complete for example. Or Head First Design Patterns. I've not finished either of them, and have yet to fully grok what I have read.

The question then is how to tackle these lists? It seems the only way to stay motivated is to come up with a schedule and then stick to it. Read one chapter a week. Write a blog post a week. Or if I work it just right, I could write a blog post on the chapter I just read. But what if I get bored with one subject? Do I switch between books each week, or should I stick to one until I finish? If I want to post on what I'm reading, how much original content is necessary and how much can I quote or link to?

I'm off to ponder these questions (or read web comics, I've not decided which.) What are your thoughts on the subject? Do you have any professional goals to tackle next year? (Or even non-professional goals?) And if so, how do you plan to keep on track?

Tuesday, November 18, 2008

Batch permission change for MSSQL stored procs

I recently ran into a batch of existing stored procs that needed additional execute permissions. After some digging on the web I was able to put together a simple script to make the changes. Since I'll likely need it again in the future, I decided to throw it out here.

Assuming all of the stored procs contain the string 'MyApp' in the name, and all need execute permissions added for 'myuser', the following will make the necessary change

  declare @SQL varchar(8000)
 
  select @SQL = isnull(@SQL, '') + 
    'grant execute on [' + 
    routine_schema + '].[' + 
    routine_name + '] to [myuser];'
  from information_schema.routines 
  where SPECIFIC_NAME like '%MyApp%' 
 
  exec (@SQL )

Thursday, September 11, 2008

Refactor - Moving duplicate code to a separate method

I've mentioned in previous posts that it pains me to see developers use copy/paste to write code. It's more error prone, as you have to make sure all necessary changes are made to the copy. It's also harder to maintain since finding a bug in one copy of the code means making changes to the others - assuming you can find those copies. Depending on the circumstances, it can also be harder to read the code. As I keep running into less-than-ideal code, I decided a few posts were in order.

Today, let's look at a method that builds an XmlDocument based on values from a database. Here, the info is passed in a DataRow object.

public XmlDocument GenerateMailingLabel(DataRow subscriber)
{
  XmlDocument doc = new XmlDocument();
  doc.LoadXml("<MailingLabel><SendTo /></MailingLabel>");
 
  XmlNode sendToNode = doc.SelectSingleNode("//SendTo");
 
  XmlAttribute attribute = doc.CreateAttribute("FirstName");
  attribute.Value = subscriber["FirstName"].ToString();
  sendToNode.Attributes.Append(attribute);
 
  attribute = doc.CreateAttribute("LastName");
  attribute.Value = subscriber["LastName"].ToString();
  sendToNode.Attributes.Append(attribute);
 
  attribute = doc.CreateAttribute("StreetAddress");
  attribute.Value = subscriber["StreetAddress"].ToString();
  sendToNode.Attributes.Append(attribute);
 
  attribute = doc.CreateAttribute("City");
  attribute.Value = subscriber["City"].ToString();
  sendToNode.Attributes.Append(attribute);
 
  attribute = doc.CreateAttribute("State");
  attribute.Value = subscriber["State"].ToString();
  sendToNode.Attributes.Append(attribute);
 
  attribute = doc.CreateAttribute("Zip");
  attribute.Value = subscriber["Zip"].ToString();
  sendToNode.Attributes.Append(attribute);
 
  return doc;
}


There's nothing terribly complex going on here. It starts by creating a new, nearly empty XmlDocument. It then pulls necessary values out of the DataRow and adds them as attributes to the <SendTo> node. Modifying existing attributes appears to be easy, as each one is isolated to a single block of three lines. Adding new attributes is similarly straightforward. Copy an existing block, paste it just before the 'return', and then modify the two string literals. If it's so easy to work with, why change the code?

First, you have to make sure you modify both string literals correctly. Fail to modify one or both of them and you have a bug that may not be caught any time soon.

Second, these types of methods tend to become long. It may be easy to read currently, but what happens when you have twenty attributes to add to the document? Picking out a particular block to modify becomes a bit more challenging. This is especially true if you're currently lacking in sleep and/or caffein.

Third, a structural change to the xml document becomes a slow, tedious task. Maybe halfway through the project someone decides you need to create sub-elements instead of attributes for each value. In the above code, you have six sets of changes to make.

A better solution would be to pull the block of three lines into a separate method:

private void CreateAttribute(string attributeName, string columnName,
  XmlDocument doc, XmlNode sendToNode, DataRow subscriber)
{
  XmlAttribute attribute = doc.CreateAttribute(attributeName);
  attribute.Value = subscriber[columnName].ToString();
  sendToNode.Attributes.Append(attribute);
}


Once we've done this, each block of code in the GenerateMailingLabel method can be reduced to a single line:

public XmlDocument GenerateMailingLabel(DataRow subscriber)
{
  XmlDocument doc = new XmlDocument();
  doc.LoadXml("<MailingLabel><SendTo /></MailingLabel>");
 
  XmlNode sendToNode = doc.SelectSingleNode("//SendTo");
 
  CreateAttribute("FirstName", "FirstName", doc,
    sendToNode, subscriber);
  CreateAttribute("LastName", "LastName", doc,
    sendToNode, subscriber);
  CreateAttribute("StreetAddress", "StreetAddress", doc,
    sendToNode, subscriber);
  CreateAttribute("City", "City", doc,
    sendToNode, subscriber);
  CreateAttribute("State", "State", doc,
    sendToNode, subscriber);
  CreateAttribute("Zip", "Zip", doc,
    sendToNode, subscriber);
 
  return doc;
}


Now adding a new attribute requires one additional line instead of three. Even better, bug fixes or structural changes will be limited to a single section of code. In either case, changes can be made faster and with less risk of introducing bugs.