Josh posted on May 4, 2008 21:11

File this under stuff I should have known...

If you only need read-only access to an XML file, using the XmlReader is much, Much, MUCH faster than loading the file into an XmlDocument object and performing XPath selects.

I have recently been working on a project where I was doing some off-brand serialization/deserialization of objects.  Initially I was simply loading the file into an XmlDocument and doing my node selects.  I had roughly 80 megabytes in 100 XML files and the performance wasn't where I wanted to be.

After doing a bit of research I found that, Duh! the XmlDocument is slow.  I adjusted my code to use an XmlReader for reading the XML files and XmlWriter for saving out the serialized XML.

The results went from 1 minutes, 8 seconds to load using XmlDocument to 8 seconds using XmlReader.

...I should have known this...


Posted in: Coding  Tags: , ,

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5
Josh posted on April 20, 2008 16:02

I love food and I love to eat.  Despite this I do not consider myself a food snob (my pickiness towards food is an indictment on condiments rather preferring high-end food).  Because I love to eat it pains me when I have to take issue with a restaurant; especially one that both Sharla and I both enjoy.

This particular "Neighborhood Bar and Grill", which I'll call "Fruit Bugs" has forced me into a love/hate relationship.

More...


Currently rated 5.0 by 1 people

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5
Josh posted on April 14, 2008 14:49

With the birth of our son I quickly started to see an increase in traffic to our family blog which I was self-hosting at the time.  The extra traffic was having a negative impact on the quality of our VOIP calls and our bandwidth in general.

I bit the bullet and bought an ASP.NET hosting package with 1and.com.  At this point our blogs were running on the SubText platform and this caused some complications with 1and1.  With 1and1 if you want to host multiple websites with ASP.NET you have to point all domains to the root of your webspace and use some sort of redirection script based on the host headers.  This wouldn't be so bad except SubText uses a fairly complex system of HttpHandlers and HttpModules that do not play well with pages outside of the SubText framework.  Additionally 1and1 had problems with applications running out of subdirectories, so if I wanted to host any other domains I would have to either cop another hosting package or do some hacking in the SubText source.

When I selected SubText I had looked at several platforms (dasBlog, Community Server, etc) but never came across BlogEngine.NET until I saw my buddy was running it.

I pulled down the latest source package of BE.N and made a few modifications:

  • Swapped the tinyMCE editor out in favor of FCKEditor (steps).
  • Modified the themes I wanted to use to suit me
  • Added a LinksList user control to add links configured in the admin portion (topic of future post).
  • Made the necessary changes for BE.N to run under Medium Trust.
    • Remove trust node from web.config
    • Update the BlogRoll control to catch the SecurityException when making the WebRequest for blog details.  This does not work under Medium trust.

After moving the package up to my 1and1 space I still got SecurityExceptions regarding FileIOPermissions.  After doing some research I found other people with similar issues and repeated calls to 1and1's support to get more details about their trust level went nowhere.

So now I'm hosted on GoDaddy.  Monthly charge is less, web interface to IIS is better, and my things work the way I want them to with no problems.


Posted in: BlogEngine.NET , Coding  Tags:

Currently rated 5.0 by 1 people

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5
Josh posted on March 3, 2008 03:58
Wow...11 months since my last post.  That is shameful.

For our family blog (linkage) we needed an external service to host our family photos.  Initially I was serving them myself on my home server, but the bandwidth quickly became too much to handle (I've also since moved the hosting of these blogs to 1and1.com to off load even more traffic).

In shopping for a photo hosting service we had the following requirements:
  • Ability to download full size images for free
  • Unlimited space
  • Archival of images via CD/DVD
  • Fairly robust security to control who can see what images/albums
  • Open access to visitors (no requirement for them register or login to view images)
  • Easy print ordering

The first service we used (and still have pics on) is www.phanfare.com.  I was referred to Phanfare by another developer at work and liked it immediately.  It's setup was fairly intuitive and aside from some nuances in the security configuration it was easy to get up and running, encumbered only by the atrocious upstream of my cable internet.

Things were rolling along swimmingly with Phanfare.  Aside from the requirements above Phanfare also provided:
  • Free DVD(s) of all images if you ever leave their service
  • Support for CNAMEs, so I could point a subdomain photos.the-collins.net and it would resolve to my Phanfare albums
  • Nice .NET-based client application to upload and manage images.
But like all good things, the owners of Phanfare decided they needed some changes.  In an effort to widen their user-base they decided to roll out Phanfare 2.0.  2.0 was deemed to be more social than the original, and I am not a social person.  The biggest change and the deal breaker for us was the requirement that visitors to your images/albums register (for free) with the site and log in.  Once registered these new users get a free gigabyte of space for their own pictures with the hope that they would upgrade to the paid service.  You could still link to individual images for use in blogs, etc, but the CNAME would no longer work. 

At this point I'm searching for other services.

Surprisingly there are not a ton of services out there that meet our requirements.  One that does is SmugMug.com.  SmugMug is pretty awesome.  On top of our requirements SmugMug brings the following things to the table:
  • Tons of customization.  Ability to provide your own CSS and JavaScript allow you to really make your images your own.  For instance by default any visitor to a gallery has access to bread crumbs to navigate upwards inside your images.  Generally when we send out a link we want to keep people contained to the level of the link and anything below it.  With a bit of DHTML I was able to change the site to deactivate the links if the visitor is not logged in.  Very sweet.
  • Lots of photo upload options.  There is a tool from the Windows context menu, a browser-based Java uploader, and others.  There is even a tool called SmuggLr which allows you to automatically migrate photos from other popular sites (including Phanfare) into SmugMug.  I was able to move about 1650 images (over 3 gigs) in about 4 hours using the tool.  Much better than manually downloading from Phanfare and uploading back up to SmugMug.
  • Great support.  I have yet to send an email that was not responded to fully and competently in less than an hour.

'Gangsta Jackson' approves of SmugMug.com

Time for a shameless plug.  If you take a look at SmugMug and like it, use my referral link and save yourself $5 off your first year of the service.

Posted in: General  Tags:

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5
Hey, I'm Josh.  A software developer currently working for a large financial institution in Charlotte, NC.

I concentrate on .NET development (C#) of both the web and winform persuasion.

Sometimes I come across problems who's solution isn't readily found on the web.  I'll blog about such things, make myself feel smart, and maybe help someone else out along the way.


Posted in: General  Tags:

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5
Josh posted on April 1, 2007 22:47
A new project I've been toying with at work is CruiseControl.Net.  CCNet is an open source continuous build integration server that is highly configurable out of the box and provides plenty of room for extension using their object model.

With minimal configuration I was able to set the server up to:
- Poll the CVS source repository for changes.
- Build our project using the already installed DevEnv.  CCNet also supports Nant and MSBuild.
- Execute our unit tests with NUnit.
- Tag the successfully built files in CVS with an automated build number.
- Send out an email to our team listing the outcome of the build including the results of the unit tests and which files changed since the last build.

Using their architecture I was able to create a custom task to run after the source files were updated to change the version in the AssemblyInfo.cs to automated build version.  I ran into a few 'gotchas' along the way developing this task and thought I'd share my experience and approach to this problem.

Task Requirements:
- Files are under CVS source control, so the task must interact with the source control to properly Update, Edit, Change, Commit, Unedit the file.
- AssemblyInfo.cs files should not be modified if the code on server is not currently compiling.
- The modifications to the assembly info classes should not be perceived as relevant changes to the CruiseControl server, thus creating a situation where builds are attempted with no pertinent source files being changed.
- Have the CCNet server include the assembly version in the results email.

Creating the Custom Task:
The developers of CCNet have provided an interface ITask that you must implement in order to successfully integrate your code with the CCNet server. 

The only required method in the ITask interface is:

public void Run(IIntegrationResult result)

Simple enough, so we create a class AssemblyVersionTask and implement the ITask assembly.

NOTE:  The output assembly must be named
ccnet.*.plugin
and be placed inside the
CCNet\server 
directory for it to be found by the server.

The Execute method hands us an object that implements IIntegrationResult.  This object contains all of the necessary information about the current build needed to perform a task, such as root directory of project we are building, build number that is in progress, current state of the build, and a collection of TaskResults that are used on the CCNet dashboard and in the results emails.

In my case, I needed two other external pieces of information to be able to complete my task, the path to the CVS client on the CCNet server, and the mask to generate the correct version for the assemblies.  We are able to specify these values as part of our task's configuration in the ccnet.config.

Configuring the CruiseControl Server for our Task:
CCNet uses a project called NetReflector to dynamically load objects and populate configuration values.  Below is what the configuration looks like for my task:

<assemblyversion>
<versionMask>1.0.0.*</versionMask>
<pathToCVS>c:\program files\cvsnt\cvs.exe</pathToCVS>
</assemblyversion>

In my class I use NetReflector attributes to bind this configuration to my task:

 using System;
using System.Collections;
using System.Collections.Specialized;
using System.IO;
using System.Threading;
using Exortech.NetReflector;
using ThoughtWorks.CruiseControl.Core;

namespace ccnet.AssemblyVersionTask.plugin
{
  [ReflectorType("assemblyversion")]
    public class AssemblyVersionTask : ITask
    {
        [ReflectorProperty("versionMask", Required=true)]
        public string VersionMask;
 
        [ReflectorProperty("pathToCVS", Required=true)]
        public string PathToCVS;

        .....

The ReflectorType attribute ties the "assemblyversion" node to our class and ReflectorProperty populates our public members with the values from the configuration.

The high level view of the task's processing is as follows:

public void Run(IIntegrationResult result)
{
StringCollection assemblyInfoFiles = GetAssemblyInfoFiles();
ModifyFiles(assemblyInfoFiles);
result.MarkStartTime();
result.AddTaskResult("<AssemblyVersionResult>All output assemblies marked version " + versionToSet + "</AssemblyVersionResult>");
}

I get a collection of all AssemblyInfo.cs files in my project's tree and then modify them.  Now it gets interesting. 

Remember requirement 3, about not forcing us into a constant loop of builds because the changes my to AssemblyInfo.cs would register as modifications to our project?  This is where
result.MarkStartTime()
comes in.  This updates the start time of the current build to the current time such that when the next build is run it will look for changes that occurred after the previous start time.  We are now assured that the only changes the server picks up as a modification are valid changes to the source, not changes to the AssemblyInfo.cs by our custom task.

result.AddTaskResult
adds a chunk of XML to the build log which we will later retrieve.

Configuring the Email Output:
Since we've added a node to the output <AssemblyVersionResult>, we can configure the server to include this information in the results email.

First we must create an XSL to define the the format of our message:

<?xml version="1.0"?>
<xsl:stylesheet
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:output method="html"/>
    <xsl:variable name="resultsnode" select="//AssemblyVersionResult"/>   
    <xsl:template match="/">
        <xsl:if test="count($resultsnode) > 0">
            <br />
            <table cellpadding="2" cellspacing="0" border="0" width="98%">
            <tr>
                    <td class="sectionheader" colspan="2">
                         Assembly Versioning
                </td>
         </tr>
         <tr>
            <td><xsl:value-of select="$resultsnode"/></td>           
            </tr>
            </table>
        </xsl:if>
    </xsl:template>
</xsl:stylesheet>

The XSL file should be saved to the CCNet\server\xsl directory.

Then in the ccnet.exe.config the new XSL should be added to the list XSL files that are applied to the email results:

<xslFiles>
....
<file name="xsl\assemblyversion.xsl" />
....
</xslFiles>

All Done:
This guide serves as a high-level explanation on getting custom tasks configured and running within the CruiseControl.Net server.  I glossed over the actual guts of my task as the setup of the task cause me more problems than the actual coding of the work the tasks performs.

Feel free to shoot me a message if you have any questions.

- jc

Posted in: Coding  Tags:

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5
Disclaimer
The opinions expressed herein are my own personal opinions and do not represent my employer's view in anyway.

© Copyright 2010 Josh C.