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.

Friday, August 29, 2008

Verify user membership in an AD group

Occasionally, when working on a WinForms application, I need to validate the current user against an Active Directory group before letting the application run. Though not a complex task, it usually takes me a bit of time to research (Translation: I have to copy code from several web sites and modify to suit my needs.) So I figured I'd post my own implementation here for others to use in their research. That, and maybe save myself a bit of time in the future.

The first step is to get the list of AD groups the current user belongs to. The magic necessary to make this happen is contained in the System.DirectoryServices namespace. The code to return the list looks like so:


internal static List<string> GetADGroupsForCurrentUser()
{
List<string> groups = new List<string>();
string domainName = Environment.UserDomainName;
DirectoryEntry entry = new DirectoryEntry("LDAP://"
+ domainName);
string filter = "(samAccountName="
+ Environment.UserName + ")";

DirectorySearcher search =
new DirectorySearcher(entry, filter);
search.PropertiesToLoad.Add("memberOf");

SearchResult results = search.FindOne();
int groupsCount = results.Properties.Count;
for (int i = 0; i < groupsCount; i++)
{
string groupString =
results.Properties["memberOf"][i].ToString();
groups.Add(groupString);
}

return groups;
}

Once we have the list, we need to loop through it looking for the desired AD group. The strings returned from the above code contain comma-delimited properties (something like "CN=MyDomainGroup,OU=Security Groups,DC=local".) Because of this I'm using the Contains method to search the string for the desired domain name. I'm sure there's another way to do this, but whatever works...


public static bool CurrentUserIsAuthorized(string authorizedGroupName)
{
List<string> activeDirectoryGroups =
GetADGroupsForCurrentUser();
foreach (string group in activeDirectoryGroups)
{
if (group.Contains(authorizedGroupName))
return true;
}

return false;
}

Wednesday, August 27, 2008

Intellisense for WCF config files

I found it slightly irritating that there is no intellisense for WCF config files built into VS2005. Fortunately a quick Google search turned up a blog post with a solution. I figured I'd post it here so I can find it later. That, plus it allows me to meet my self-imposed quota of at least one blog entry per month :)

The solution can be found here.

Sunday, July 13, 2008

Don't make me GAC

We discovered an interesting "feature" in VisualStudio 2005 a couple days ago. Let's say I have a solution containing a Utilities project and a web site referencing that project. The Utilities assembly references a third-party dll (log4net in this case.) When the web site is compiled, it automatically pulls any dependent files into its 'bin' folder. Note the presence of log4net.dll


This seems fairly straightforward. But let's say I then load the solution on another computer (a build server perhaps.) The code compiles as before, but this time it doesn't pull in log4net.dll


At this point, I could manually copy the dll into the folder, right? That was my first thought, which of course proved to be wrong. The reason is that we also have an install project for the site. The installer is set to use all output files from the web project. If we add files to the site, the installer will automatically include them in the next compile. Unfortunately, when I manually add the dll to the bin folder, the installer overlooks it.

My next thought was to directly reference the log4net.dll in the web project.



Which still compiles on my box. And still ignores the dll on the build server. At this point, we opened up the references dialog on the build box. Much to our surprise, the dll is being referenced in the GAC


It seems that when a web project loads, it first looks in the GAC for referenced assemblies. If it finds them there, it ignores the files originally pointed to. Because of this, VisStudio doesn't copy the files to the bin folder. Which of course means the installer won't contain the dll.

Ideally, the dll shouldn't have been GAC'd on the build server. Since removing it from the GAC would cause us other issues, our workaround was to hard-code log4net.dll in the install project.

Note: The web site was built using the out-of-the-box project template, which doesn't create an actual .proj file. Microsoft later released a Web Application Project (with VS2005 SP1) that does create a .proj. It's possible that this would force the dll to load from the file reference, but I've not tested that theory.

Sunday, June 22, 2008

Wrapping a class in a singleton

My current task at work has me building a new application, using an existing framework as the base. One of the classes in this framework wraps access to the config file (which is actually kept in the database.) As soon as this class is instantiated, it retrieves the config info from the database. I wanted a way to keep a single instance around for the lifetime of the application. Otherwise, I'd be hitting the database multiple times to pull down the same information.

One option would have been to create an instance in the app's constructor. The downside to that approach is that I'd have to pass it into every method that might need it. Instead I chose to use the singleton pattern.

Below is a quick example. Marking the MyConfig class as static means it can't be instantiated. The static constructor will be called once and only once - the first time the class is accessed. This constructor creates an instance of the CommonConfig class, which can be accessed through the Config property. Now, regardless of how many times my code accesses MyConfig.Config, the database is only hit once.

    public static class MyConfig
    {
        static readonly CommonConfig _commonConfig;
 
        static MyConfig()
        {
            _commonConfig = new CommonConfig();
        }
 
        public static CommonConfig Config
        {
            get
            {
                return _commonConfig;
            }
        }
    }

Friday, May 9, 2008

Add scroll wheel support to VB6

Yes, you read that right. The client I'm currently assigned to has a significant amount of legacy code in VB6. Not an ideal situation, but at least it isn't COBOL.

Anyway, one of the first things I noticed after loading a project was that I couldn't scroll through the code using the mouse wheel. It turns out the VB6 IDE didn't include wheel support. Fortunately, there's an add-in from Microsoft that fixes this. See KB article 837910 for the download and install instructions.

And yes, I'm aware that this probably doesn't pertain to you (if it does, I feel your pain.) The post is mainly a bookmark for my own reference. Any sympathy it generates is just a bonus :)

Thursday, May 8, 2008

DotNetMigrations

A friend of mine recently ported the Ruby on Rails "Migrations" tool to .Net. Assuming you can get all of the developers on a team to script out their database changes (and matching rollbacks) this tool will make it easier to build, update and deploy your database. It also becomes trivial to revert the changes when something breaks unexpectedly.

You can find details here.

Adding entries to a file's right-click menu

When setting up a new developer computer, there are a number of tools and shortcuts I typically want. One of these is the ability to register COM dlls through Explorer's right-click menu. The easiest way to add this is through the registry.

Standard warning: I'm sure you're aware that messing with the registry may cause all sorts of problems. Proceed with caution. Unless of course it's not your computer you're messing with. In which case, hack away.

Start regedit from the Run dialog (keyboard shortcut: Win+R.) Under HKCR (HKEY_CLASSES_ROOT) locate the extension of the file to modify - .dll in this case. In the list of properties, you should see a "(Default)" string with the internal name of the file type. For dll it will be "dllfile."


Further down, still under HKCR, will be an entry for dllfile. This is where the changes will be made.


Under dllfile, expand the "shell" key. Add a new key and set the name as you want it to appear on the right-click menu (mine is "register".) Within that, add another key with the value "command." The "(Default)" string needs be set to the command that you want to run. For this exercise it will be:

regsvr32.exe "%1"

Regsvr32 is the command-line utility to register the COM component. The %1 in quotes tells Windows to pass in the filename and path as the first parameter.


Once this is done, you should be able to right-click on any dll and see the new entry.


This takes care of one machine. Now that you have the shortcut defined, it would be useful to copy this to other machines. To do so, right-click on the "register" key and choose Export. Saving the selected branch will give you a .reg file with the changes. Copy the file to another computer and double-click to import it into the registry of that computer.

Friday, May 2, 2008

Metadata? But the code's right there...

Here's a highly complex bit of code I've been working on


In this example I have a Customer class in MyAssembly2, which you can see is a separate project in the solution. On the main form of the application I'm making a call to the Customer instance method SaveToDatabase. If I right-click on the method call and choose "Go To Definition" I'm taken to a page of metadata.


This lets me see method signatures but not the code within those methods. If I were to try the same thing for my Utilities class, however, I'm taken to the .cs file within MyAssembly1. So what's the difference?

Here are the reference properties for the two assemblies:


The two are almost identical, but if you look closely, you'll see that MyAssembly2 has a "Specific Version" property where MyAssembly1 does not. This is because MyAssembly2 was added as a file reference - we pointed VisualStudio at the compiled dll instead of the project. Because of this, VS doesn't know that the dll and project are really one and the same.

The fix is a simple matter of deleting the reference to MyAssembly2 and re-adding - this time as a project reference.

Tuesday, April 29, 2008

Everyone should be good at something

Anyone who's worked with me in recent years knows how much I love deleting code. It could be removing something that's obsolete or refactoring to reduce duplication. Or maybe the requirements change mid-project. Needless to say, some question whether my SLOC (source lines of code) output is positive or negative.

Regardless, I stumbled upon a shortcut in VisualStudio (Shift + Del) that deletes the entire line where the cursor is located. No more selecting the whole line and then pressing Delete. I've become a code-deletion ninja!

Friday, March 14, 2008

Unlocking files when Visual Studio can't

There's an option in Visual Studio that, on compile, will pull all of your XML comments out into separate files. These can then be combined into a help file using a tool like Sandcastle. Most of the time this feature works correctly. If you cancel a build in process, or the compile fails, VS occasionally fails to release the file lock. From that point on, the compile fails with an error stating the file is in use by another process. I've not found a way within VS to force an unlock short of closing the app.

There is, however, a faster way using Process Explorer. Start Process Explorer and select Find > Find Handle or DLL... from the menu. Enter all or part of the filename currently locked, then click Search. You should see the guilty process - devenv.exe in this case.


Double-clicking on the entry will display a list of handles for that app, with the selected handle highlighted. Right-click on the selection and choose Close Handle. You'll see a dialog warning about potential crashes or system instability (meaning don't try this with a system file.) Tell it to continue and the handle will be closed. VS will again compile without issue.

Tuesday, February 26, 2008

I can read the file myself, thank you

I've been downloading a number of PDFs lately, and the one thing that has been bugging me is the slow performance of Adobe Reader. Fortunately, I've found at least one of the major causes - the accessibility features. When opening a PDF, I was occasionally presented with a dialog stating "Please wait while the document is being prepared for reading." A quick search on the web located others with the same problem. This post, along with one of the comments, led me to the solution:

Locate the install folder (C:\Program Files\Adobe\Reader 8.0\Reader\plug_ins) and delete the following files

  • Accessibility.api

  • MakeAccessible.api

  • ReadOutLoud.api


Though I didn't break out the stopwatch, the performance improvement was significant. Before, there would be a pause after scrolling more than a page or two. Now, I can hold the PageDown key and see dozens of pages fly by before any pause (probably to allocate more memory.)

Tuesday, February 12, 2008

Automate config changes for different environments

Most applications I've worked on contain one or more config files. These are generally used to track web service locations, database connection strings, and logging information. Keeping these settings outside the code makes it easy to modify them without a recompile. This is especially useful when moving a deployment from dev to test, or from test to production.

On most of these projects, the preferred method used by developers is to keep multiple config files in the source repository. These are distinguished by simple renaming of the files (such as app.config, app_qa.config, app_prod.config.) In theory this should work - once you have the config files set up for each environment, deployment requires replacing the file as appropriate. And if you use a .bat file or build script, the file rename will be automatic.

The scenario usually overlooked, however, is the case where something in the config changes. Maybe a new web service is being used by the application. Or perhaps a new assembly needs to be referenced. In my experience, the developer often makes the changes to the main file (app.config), but forgets to make the same changes to the others. During development the code appears to work fine. Unfortunately, the app fails to run in the QA environment. Usually, some amount of time is spent by the developer before realizing that the problem is with the config.

To reduce this sort of issue, I recommend only keeping one config in the source repository. This config will contain the values necessary to run in the development environment. During the build process, the config is updated for the target environment. The MSBuild Community Tasks contains a FileUpdate task that makes this easy.

Let's say you have a config file with the following:


<appSettings>
<add key="MyWebService" value="http://localhost/service/MyWebService.asmx" />
</appSettings>

The msbuild script to deploy to QA would contain:


<PropertyGroup>
<MyAppConfigFile>c:\source\MyApp\app.config</MyAppConfigFile>
<MyWebServiceRegex>http://.*/service/MyWebService.asmx</MyWebServiceRegex>
<QaWebServicePath>http://QaServer/service/MyWebService.asmx</QaWebServicePath>
</PropertyGroup>
<Target Name="ConfigureQA">
<FileUpdate
Files="$(MyAppConfigFile)"
Regex="$(MyWebServiceRegex)"
ReplacementText="$(QaWebServicePath)" />
</Target>

Thursday, January 3, 2008

A twisted, yet valid observation

Today, I again saw what is an all too frequent occurence in some dev shops - a failed build.

Rather, a grossly negligent failed build. One that could have easily been avoided. So to that developer, and others of similar ilk, I send this message:

I know you only changed one line in a single file. And I'm aware it takes ten minutes to build the solution on your machine. I even realize that no one was going to pull down code at that precise moment.

Nothing, however, pains the resident Build Guy more than that big red Build Failed icon. Every time I see that, a little piece of me dies.

And no, Fellow-Programmer-Who-Shall-Remain-Nameless, remembering to build immediately after checking in the file is not "close enough." Sure you'll have the fix checked in even as the build reports the failure. And sure, any other time of the day I would have been wandering the halls and never would have known. But apologies won't help. It's still too little, too late. It's like applying the brakes after running over the cat...

Tuesday, January 1, 2008

Customizing DataTips

A convenient debugging tool in Visual Studio is the ability to hover the mouse over an object and see the information currently stored there. This is referred to as a DataTip.


If the class is derived from a base class, or contains other objects within, you can drill down to see those values as well. Depending on the complexity of the object however, this can become tedious. This is especially true if there are a couple values that you always want to see when debugging.

To quickly see one or more properties on a class, you can do so using the DebuggerDisplay attribute in System.Diagnostics. If I add the following to MyBaseClass, I can view the value of X without expanding the DataTip. (The property to display is placed in braces.)


[DebuggerDisplay("The value of X is: {X}")]


If I derive a new class from MyBaseClass, I can set a DebuggerDisplay attribute that accesses values both from this class and the base.



[DebuggerDisplay("{X}, {Y}")]
class MyDerivedClass : MyBaseClass
{
private int _y;
public int Y
{
get { return _y; }
set { _y = value; }
}
}