I want to start backing up my home machine hard drive. There are a bunch of services out there and some of them use size-based storage – the more space you use, the more they charge you. When you look in Windows Explorer, there is not an easy way to determine the size of a given directory – just file sizes:

Since I am not backing up the entire drive, I can’t look at the drive size and space used to determine how much a storage company will cost – so I can’t adequately price compare.

I then thought of whipping up a quick recursive function to get the cumulative size of all of the files in a directory. I then thought I could use the exercise to investigate recursion in WF.
I set up a test location on my file system (I know, I should be using a Mocking Framework):

I then wrote a function that gets the total size of all of the files in a directory (and any subdirectory) like this:
static long GetFileSize(string directoryName)
{
long cumulativeSize = 0;
foreach (FileInfo fileInfo in new DirectoryInfo(directoryName).GetFiles())
{
cumulativeSize += fileInfo.Length;
}
return cumulativeSize;
}
I then wrote a unit (integration, really) test to verify the function as it is currently written:
[TestMethod()]
[DeploymentItem("Tff.DirectorySize.exe")]
public void GetFileSizeTest()
{
string directoryName = @"C:\TestDirectory";
long expected = 12647;
long actual = Program_Accessor.GetFileSize(directoryName);
Assert.AreEqual(expected, actual);
}
I then added the recursion bit into the function:
static long GetFileSize(string directoryName)
{
long cumulativeSize = 0;
DirectoryInfo directoryInfo = new DirectoryInfo(directoryName);
foreach (FileInfo fileInfo in directoryInfo.GetFiles())
{
cumulativeSize += fileInfo.Length;
}
foreach (DirectoryInfo subdirectoryInfo in directoryInfo.GetDirectories())
{
cumulativeSize += GetFileSize(subdirectoryInfo.FullName);
}
return cumulativeSize;
}
Still got green on my original unit test. I then added a new subdirectory to the test directory and copy/pasted the original test files into the test subdirectory. I changed the unit test to be like so:
[TestMethod()]
[DeploymentItem("Tff.DirectorySize.exe")]
public void GetFileSizeTest()
{
string directoryName = @"C:\TestDirectory";
long expected = 12647 * 2;
long actual = Program_Accessor.GetFileSize(directoryName);
Assert.AreEqual(expected, actual);
}
I still got green. This is a great exercise to show the problems with mixing integration tests with unit tests and the usefulness of Mocking frameworks.
I then added a WF Activity to the project. Interestingly, I could not add a variable to this blank activity.
I added a Flowchart activity to the project and added a local variable called cumulativeSize. A pain-in-the-butt feature of WF is that long (and most other types) and not available in the select box.

When browsing, remember to look in MSCorLib and System Namespace

If you forgot that long is actually System.Int64
, In
Intellisense can help you:

In any event, the 1st line of the function was replicated in WF:

I then realized I need to set up the input parameters of the function/flowchart. Easy Enough:

I then looked at the next line of the function:
I need a way of representing an instantiated DirectoryInfo object in my WF using that input argument. I realized that I need to wrap the DirectoryInfo class with an activity to use it. I dropped in a code activity thinking that all I had to do was implement the IDirecotryInfo interface. Note that to avoid name collisions, I called the activity “DirectoryInfoActivity”, not “DirectoryInfo”.
Alas, there is no such thing as a IDirectoryInfo interface. DirectoryInfo inherits from FileSystemInfo and is a sealed class. Ugh! If it was at least a partial class, I could reverse-engineer the interface (like I do using EF4), but that is out.
I then realized that I should just make the DirectoryInfo class a local variable for the workflow:

I could then do a For..Each activity on that local variable. A couple of key points:
You need to set the property of the ForEach Type to System.IO.FileInfo like so:

Just changing it in the Designer <FileInfo> doesn’t do anything.
Also, working in VB syntax in little text boxes can be very frustrating. Using the elipises in the property window is very helpful:

Next, I created a Unit Test (manually, a context menu with Create..UnitTest would be very helpful Microsoft). I had to lookup that the WorkflowInvoker is in System.Activities:
[TestMethod]
public void GetFileSizeTest()
{
string directoryName = @"C:\TestDirectory";
long expected = 12647;
GetFileSize getFileSize = new GetFileSize();
getFileSize.directoryName = directoryName;
IDictionary<string, object> results = WorkflowInvoker.Invoke(getFileSize);
long actual = Int64.Parse(results["cumulativeSize"].ToString());
Assert.AreEqual(expected, actual);
}
The first time I ran it, I got a null reference exception -> apparently you have to New up your objects – WF doesn’t do that for you:

And then we get a pass:

The next challenge is to add in the recusion. I put in the next For..Each Loop:


But the problem is that the right-hand side needs to invoke the workflow again. I don’t see any InvokeWorkflow activies in the toolbox, so I made a code activity. This post is taking longer than I want, so I’ll drop in the remaining tomorrow.