Home

Archived Posts from “How I Did It”

TortoiseSVN - Change User After Save Authentication

31

July

When you first access your SVN repository you are given the option to save your authentication information.   Nice feature, right?   Well, what if you need to access the repository via different credentials?  image

Here’s the trick:

  1. Right-click on the root folder
  2. Select TortoiseSVN
  3. Select Settings
  4. Navigated to Saved Data
  5. Clear the stored Authentication Data

The next time you need to access the repository, you will be prompted for your new credentials.


Export GridView to Excel within an UpdatePanel

25

July

There’s a ton of information online about exporting a DataGrid or GridView to Excel, but most variations do not consider the GridView may reside within an UpdatePanel.  It goes without saying, but I was disappointed when I recently dusted off my “Export GridView to Excel” code snippet and encountered a number of exceptions.  So I revisited a number of links and I collected a working solution.  If you need a “simple” way to export your GridView to Excel and you are using an UpdatePanel, I hope the following code finds you well:

protected void btnExport_Click(object sender, EventArgs e)
{
    // Reference your own GridView here
    if (AccountGrid.Rows.Count > 65535)
    {
        DisplayError(“Export to Excel is not allowed” +
            “due to excessive number of rows.”);
        return;
    }

    string filename = String.Format(“Results_{0}_{1}.xls”,
        DateTime.Today.Month.ToString(), DateTime.Today.Year.ToString());

    Response.Clear();
    Response.AddHeader(“Content-Disposition”, “attachment;filename=” + filename);
    Response.Charset = “”;

    // SetCacheability doesn’t seem to make a difference (see update)
    Response.Cache.SetCacheability(System.Web.HttpCacheability.NoCache);  

    Response.ContentType = “application/vnd.xls”;

    System.IO.StringWriter stringWriter = new System.IO.StringWriter();
    System.Web.UI.HtmlTextWriter htmlWriter = new HtmlTextWriter(stringWriter);

    // Replace all gridview controls with literals
    ClearControls(AccountGrid);

    // Throws exception: Control ‘ComputerGrid’ of type ‘GridView’
    // must be placed inside a form tag with runat=server.
    // ComputerGrid.RenderControl(htmlWrite);

    // Alternate to ComputerGrid.RenderControl above
    System.Web.UI.HtmlControls.HtmlForm form
        = new System.Web.UI.HtmlControls.HtmlForm();
    Controls.Add(form);
    form.Controls.Add(AccountGrid);
    form.RenderControl(htmlWriter);

    Response.Write(stringWriter.ToString());
    Response.End();
}

private void ClearControls(Control control)
{
    for (int i = control.Controls.Count - 1; i >= 0; i–)
    {
        ClearControls(control.Controls[i]);
    }

    if (!(control is TableCell))
    {
        if (control.GetType().GetProperty(“SelectedItem”) != null)
        {
            LiteralControl literal = new LiteralControl();
            control.Parent.Controls.Add(literal);
            try
            {
                literal.Text =
                    (string)control.GetType().GetProperty(“SelectedItem”).
                        GetValue(control, null);
            }
            catch
            {}
            control.Parent.Controls.Remove(control);
        }
        else if (control.GetType().GetProperty(“Text”) != null)
        {
            LiteralControl literal = new LiteralControl();
            control.Parent.Controls.Add(literal);
            literal.Text =
                (string)control.GetType().GetProperty(“Text”).
                    GetValue(control, null);
            control.Parent.Controls.Remove(control);
        }
    }
    return;
}
 

Update: 7/30/2008

I previously noted that SetCacheability doesn’t seem to make a difference.  Well, I was right…until I deployed my code to a site behind SSL.  As it turns out, in order for Internet Explorer to open documents in Office (or any out-of-process, ActiveX document server), Internet Explorer must save the file to the local cache directory and ask the associated application to load the file by using IPersistFile::Load.

http://support.microsoft.com/default.aspx?scid=KB;EN-US;q316431&

If the file is not stored to disk, this operation fails. When Internet Explorer communicates with a secure Web site through SSL, Internet Explorer enforces any no-cache request. If the header or headers are present, Internet Explorer does not cache the file. Consequently, Office cannot open the file.

RESOLUTION: Web sites that want to allow this type of operation should remove the no-cache header or headers. In other words, comment out the following line of code particularly if you are running under SSL:

Response.Cache.SetCacheability(System.Web.HttpCacheability.NoCache); 

Additional Comments

Per Jinath Blog, if you are using an UpdatePanel, you may get a System.WebForms.PageRequestManagerParserErrorException exception.  The solution is to add a PostBackTrigger and give it’s ControlID as the excel export button’s ID or you can move your excel export button out side of the update panel.  I verified both options and they work great.  I ultimately went with the former option as such:

    ...
    </ContentTemplate>
    <Triggers>
        <asp:PostBackTrigger ControlID=”btnExport” />
    </Triggers>
</asp:UpdatePanel>

Per ASPAlliance, you may encounter issues a number of issues which require the following solution.  Check out the link (and the comments) for more details if you get stuck.  I only encountered #1 on the list.

  1. You may get an exception which states your Control ‘Grid’ of type ‘GridView’ must be placed inside a form tag with runat=server.  I overcame this by dynamically adding a form to the page and then the GridView to the form before RenderContent().  This solution came per the aforementioned post’s comments.
  2. You may need to included the following page directive: EnableEventValidation=”false”.  I didn’t need to include this directive.
  3. You may need Override the VerifyRenderingInServerForm Method.  I didn’t need to do so because I added my GridView control to a “mocked” form.
     

Per Dipal Choksi, one can format the spreadsheet results in a generic manner by replacing all controls within the GridView with Literals.  This is reflected in the ClearControls() method above.  My prior implementation merely cleaned up the links associated with the sort functionality tied to the sortable headers.  This solution tackles all cells.

Additional Reference: GridViewGuy 

kick it on DotNetKicks.com


TSQL - Self Update

25

July

This morning I needed to compose a very simple SQL routine and it took me around five compiles until I got the syntax right.  All I needed to do was transfer an active status from one entity (in my case a computer) to another.  I decided to implement this by updating the same table in which I was selecting.  I think the syntax associated with "self update" type queries is tricky so this post is so I personally don’t loose this code snippet and future cycles.  I hope it might help you as well.

This example transfers the status of the source computer to the destination computer.  Nothing fancy…

DECLARE @SourceID INT; SET @SourceID = 2
DECLARE @DestinationID INT; SET @DestinationID = 1

DECLARE @Computer TABLE (ComputerID INT, StatusID INT)

INSERT INTO @Computer(ComputerID, StatusID)
SELECT @DestinationID, 1 UNION SELECT @SourceID, 2

SELECT * FROM @Computer

UPDATE A
SET A.StatusID = B.StatusID
FROM @Computer A, @Computer B
WHERE B.ComputerID = @SourceID
AND A.ComputerID = @DestinationID

SELECT * FROM @Computer

(2 row(s) affected)
ComputerID  StatusID
----------- -----------
1           1
2           2

(2 row(s) affected)

(1 row(s) affected)

ComputerID  StatusID
----------- -----------
1           2
2           2

(2 row(s) affected)

 

kick it on DotNetKicks.com


LinkButton Image

02

July

I am not a CSS expert, but the following technique can be used to easily apply an image next to virtually any link. 

First you need to define your CSS class which sets the background to a referenced image. 

.icon-cancel {
    background: url(../images/icons/cancel.gif) no-repeat left top;
    display:inline;
    padding-left:18px;
    margin-left: 10px;
    padding-right:10px;
}

Once defined, assign the class to your hyperlink.

<a href=”#” class=”icon-cancel”>Cancel</a>
 

You may need to play with padding and margin, but you will end up with something similar to the following result:

image 
 

I used this technique to pretty up a very plain GridView display by applying classes to my edit, update and cancel linkbuttons today.  All it took was the creation of a few more CSS classes and linkbutton class tag updates.  image

 
 
 
 
 
 
 
 
 
 
<asp:TemplateField ShowHeader=”False”>
    <EditItemTemplate>
        <asp:LinkButton ID=”LinkButton1″ runat=”server” CausesValidation=”True”
            CommandName=”Update” class=”icon-save” Text=”Update”></asp:LinkButton>
        <asp:LinkButton ID=”LinkButton2″ runat=”server” CausesValidation=”False”
            CommandName=”Cancel” class=”icon-cancel” Text=”Cancel”></asp:LinkButton>
    </EditItemTemplate>
    <ItemTemplate>
        <asp:LinkButton ID=”LinkButton1″ runat=”server” CausesValidation=”False”
            CommandName=”Edit” class=”icon-edit” Text=”Edit”></asp:LinkButton>
    </ItemTemplate>
</asp:TemplateField>
 

kick it on DotNetKicks.com


Custom Templates with SubSonic

09

June

Over the weekend, I found myself doing a lot of grunt work creating more than my fair share of data transfer objects which, for me, are nothing more than custom collections which contain very simple objects exposing public properties.  Simple, but cumbersome enough that I wanted to generate the classes.

I have been working with SubSonic for a few weeks now but I didn’t realize the tool could generate files based on custom/personalized templates.  It’s a good thing Jon Galloway still tolerates my stupid questions or I might still not know…

Here’s how it is done using the base SubSonic install (assumes Sonic.exe and the default templates can be found under c://program files/.)

  1. Make a copy of the default templates folder as found in C:\Program Files\SubSonic\SubSonic 2.0.3\src\SubSonic\CodeGeneration\Templates.  I named my copied folder "SimpleClassTemplates."
  2. Open the aspx files in Visual Studio and modify to your heart’s content.  For example, I wanted an alternate C# class template so I modified CS_ClassTemplate.aspx.
  3. I am going to assume you are already familiar with code generation with SubSonic.  I perimagesonally like to setup a Visual Studio External Tool to allow for quick, pre-configured regeneration.  Otherwise, the following can be ported over to the command line.  Here’s the External Tool setup instructions:
    • Tools > External Tools > Add
    • Title: SubSonic Classes
    • Command: C:\Program Files\SubSonic\SubSonic 2.0.3\SubCommander\sonic.exe
    • Arguments: generate /out Generated /namespace NAMESPACE /server SERVER/db DATABASE where NAMESPACE, SERVER and DATABASE are replaced accordingly.
    • Initial Directory: $(ProjectDir)
    • Check "Use Output window" and "Prompt for arguments."
    • Select Apply or OK
  4. Select the project which will contain the "Generated" folder and auto-generated files.  Select Tool > SubSonic Classes.
  5. Enjoy. 
kick it on DotNetKicks.com


SubSonic and SSMS Tools Pack Saved The Day

21

May

Today I woke up feeling uneasy about the direction of my current project.  For all intents and purposes, I am taking an existing system which was pieced together by a couple of past developers and extending it. I’m not saying the code I  inherited was a disaster but I am not going to publicly praise the design and implementation either.  The big problem is the existing system works.

I know that last statement sounds funny but put yourself in the position of my customer.  They currently have a working product which they wish to extend and private label.  With the core functionality already in place (read: tested with dozens if not hundreds of users,) how much time could/should it take to implement my updates?

While ramping up, I jotted down a number of areas where the application could be improved but I tabled every task which wouldn’t get me closer to completing my assignment.  In other words, if the change was transparent to my client, I didn’t muck with it — for the most part.

Which brings me to this morning, approximately two weeks into my development, and my uneasy feeling.  The bottom-line is I made the wrong call.  I should have made the product my own, so to speak, and applied a handful of design changes right off the bat.  If I were completing a simple bug fix, this approach would have been wrong, but since I am scheduled for appropriately 2 months of dev, I can "afford" to make the needed improvements.  It makes sense as it would ultimately save me effort and frustration while costing my customer no extra time or money.  

Today I talked myself into thinking that refactoring/redesigning would be best for everyone and I promptly redesigned the database (all of it) and swapped out the data access layer (all of it) with the help of SSMS Tool Packs and SubSonic, respectively.  Of course, I had help. Lots of it. Hours and hours of other people’s time…  I’ve written about Getting Started with SubSonic and though I "admire" it, I hadn’t put it to good use until this morning.  SubSonic (after a quick review which consisted of watching this webcast) generated my DAL. SSMS Tool Packs was a lucky discovery as it auto-generated my CRUD routines.  The product offers much more than this and integrates with each version of SQL Server Management Studio, but I only needed stored proc creation today.  All told the entire change took less than 5 hours.  It would have taken far less time if I didn’t have a complete brain fart and chase my tail around an incorrectly established SQLEXPRESS connection string.  Pretty amazing if you ask me!

To sum things up, thanks to Rob Conery and crew for SubSonic and thanks to Mladen Prajdic for your exceptional contributions.  They really helped me get back on track with minimal investment today.  

kick it on DotNetKicks.com


Getting Started with CruiseControl.NET

29

January

I consider myself a neophyte when it comes to continuous integration (CI), but having read up on the topic, “played around” a bit, reviewed a couple of products, and having now incorporated it into our standard development practices, I am starting to hold my own.  If nothing else, the benefits of continuous integration are now obvious and, well, I can’t imagine ever turning back. 

For me, the power of continuous integration extends far beyond build validation.  CI it has become a welcome management tool which allows me to keep my finger on the pulse of numerous developed products as well as individual developer contributions.  Though I am very interested in the overall success/fail state of the current build, equally valuable is the the notifications which email specified team members the code change log which accompanies each build. (i.e. A list modified files and associated comments since the last automated build.)  If you are responsible for overall project success (which typically includes management of deliverable, code quality, task distribution, etc) then continuous integration is for you.

As I mentioned earlier, I reviewed a few products.  Last year, I reviewed FinalBuilder which provided me a nice and easy introduction into this space.  Based almost completely on its popularly and price tag (free), I have since turned to CruiseControl.NET.  I will not claim that was able to quickly get up and running with CruiseControl.NET.  At times, I was down-right questioning if I was smart enough to figure out the product and its configuration.  But, alas, it is now running on a dedicated build machine and my team and I are all the better for it. 

Please let it be known that I am not a wizard when it comes to CruiseControl.NET.  I am, however, good at is documentation of repeatable actions and I tried to capture the steps on getting started with CruiseControl.NET below.  As always, any feedback is greatly appreciated.

By the way, if you are looking for general information on continuous integration or CruiseControl.NET, you are not going to find here.  For more information, you may wish to consult the following:  What is Continuous Integration and What is CruiseControl.NET.

That’s basically it.  Best of luck!

 

1. Find a Build Machine

Though CruiseControl.NET could be installed on a development machine, I recommended that it be installed on a dedicated build server. Your build server should have access your source control repository and an optimal STMP server.  The server should have IIS and the NET Framework installed as well.  More information on the build server to follow…

 

2. Install CruiseControl.NET

Download CruiseControl.NET Server and Dashboard and install using provided default selections. 

http://downloads.sourceforge.net/ccnet/CruiseControl.NET-1.3-setup.exe?modtime=1182463056&big_mirror=0

 

3. Install MSBuild

If you are working with .NET 2.0+ applications, you can build using MSBuild. Otherwise, you may need to learn a bit about NANT.  All of the provided sample configurations below assume you will be using MSBuild. 

The MSBuild DLL is not included in initial CCNET Server installation, but it can be downloaded from the ThoughtWorks site: http://ccnetlive.thoughtworks.com/MSBuildXmlLogger%2DBuilds/. To install, simply copy the ThoughtWorks.CruiseControl.MSBuild.dll into C:\Program Files\CruiseControl.NET\server\ location.

 

4. Install Source Control Client

You will potentially need to install your source control client on the build machine. For the samples provides, we are using Borland StarTeam 2005.  CruiseControl.NET integrates with just about every source control system I could think of, so if you are using something other than StarTeam, you will want to consult the Source Control Block documentation.

 

5. CruiseControl.NET Server Configuration

CruiseControl.NET Server is driven by CCNET configuration file settings. After the fresh install, the configuration is essentially empty and requires manual updates to the ccnet.config file itself. As far as my knowledge, there is no associated configuration editor.  First things first, crack open “C:\Program Files\CruiseControl.NET\server\ccnet.config” in Visual Studios or your favorite XML editor. (There is shortcut under All Programs > CruiseControl.NET > CruiseControl.NET Config.)

If you have a pre-configured ccnet.config file available, simply overwrite the file content. If you are starting from scratch, you may wish reference the online CruiseControl.NET configuration overview and review samples: http://confluence.public.thoughtworks.org/display/CCNET/Configuring+the+Server.  Additional example configuration files were installed on your build box as well: C:\Program Files\CruiseControl.NET\Examples.  They are worth a look.

The bad news: Though you will find examples sprinkled throughout the ThoughtWorks site and the locations noted above, you will most likely need to muck around with your own config file for a while until you get it right.  Additional samples are provide below, but just as I wasn’t able to find the magic configuration combination to get my server running right off the bat, I bet you won’t either. 

The good news: Getting the right configuration was trail-and-error process for me.  I kept the configuration very simple at first and then built upon it.  Hopefully the next steps can help you with your troubleshooting.

 

6. Start CruiseControl.NET Server

The CCNET Server is not started at the time of installation. Thus, you will need to start the service manually: Administrative Tools > Services > CruiseControl.NET Server > Select > Start the service.

If the service fails at startup, it will immediately stop and present the following generic dialogue:

The CruiseControl.NET Server service on Local Computer started and then stopped. Some services stop automatically if they have no work to do, for example, the Performance Logs and Alert service.

If you encounter this error your first stop is to consult the logs: C:\Program Files\CruiseControl.NET\server\ccnet.log. By default, the file contains verbose logging which is helpful (although somewhat difficult to navigate.)  Believe me, the logs are your fried.

Based on the log file feedback, I suggest tweaking the ccnet.config file and then restarting the service – repeating until the service is fully configured.  And, if it is easier for you, you may run the CCNET Server via the command window: CD C:\Program Files\CruiseControl.NET\server > ccnet.exe.  This approach will render errors in the cmd window rather than having to monitor the ccnet.log file.  It was a time saver for me at least.

Once the project is successfully setup, you will see something similar to the following listed in the logs:

· [CCNet Server:DEBUG] The trace level is currently set to debug. This will cause CCNet to log at the most verbose level, which is useful for setting up or debugging the server. Once your server is running smoothly, we recommend changing this setting in C:\Program Files\CruiseControl.NET\server\ccnet.exe.config to a lower level.

· [CCNet Server:INFO] Reading configuration file “C:\Program Files\CruiseControl.NET\server\ccnet.config”

· [CCNet Server:INFO] Registered channel: tcp

· [CCNet Server:INFO] CruiseManager: Listening on url:tcp://localhost:21234/CruiseManager.rem

· [CCNet Server:INFO] Starting CruiseControl.NET Server

· [PROJECTNAME:INFO] Starting integrator for project: PROJECTNAME

At this point, the Server is waiting to initiate the next build. Based on the config file <triggers> setting it could take a while to get the checkout and build to fire. Adjust the following setting to around 10 seconds while still in the testing phase:

<triggers>
  <intervalTrigger name=continuous seconds=10                   buildCondition=IfModificationExists/>
</triggers>

On a similar note, the Server restarts itself automatically each time the ccnet.config file is modified.

 

7. Potential Gotchas

I ran into a couple specific cases while debugging which I suspect could be common issues. 

1. Ensure the MSBuild timeout value allows enough time to the build to complete. This will otherwise return in a failure which wasn’t easy to troubleshoot.

<msbuild>
  <timeout>600</timeout>
</msbuild>

2. For web solutions, update the TargetPath for the Debug and/or Release build of the application to be outside of the source application directory. Otherwise, you will receive the following error:

ASPNETCOMPILER : error ASPRUNTIME: The precompilation target directory cannot be in the same tree as the source application directory.

This can be done by modifying the following in the solution file:

Debug.AspNetCompiler.TargetPath = “c:\ccnet\PrecompiledWeb\SampleWeb\”
Release.AspNetCompiler.TargetPath = “c:\ccnet\PrecompiledWeb\SampleWeb\”

Alternative, one may update the Output location found in the application MSBuild properties.

You may be wondering why an updated solution won’t be updated with the next scheduled build.  As it works out (and makes sense) only changes which are applied to source control are pulled onto the build box. 

 

8. Automating Your Builds

Once the build process is running successfully, modify the <intervalTrigger> setting to a reasonable value and then update the “CruiseControl.NET Server” Service Startup Type to Automatic and then Start the service.  I might suggest also crossing your fingers for the next day or so.

 

9. Monitoring Your Builds

The CCNet Web Dashboard Application is used for reporting a wide range of information. At one end of the scale it reports summary details of all projects being managed by CruiseControl.NET and at the other it can give specific metric output for any specific build.  The Dashboard is setup with your initial installation.  You may access the Dashboard application by navigating a location similar to the following: http://buildbox.com/ccnet/ViewFarmReport.aspx.  For more information on the Web Dashboard: http://confluence.public.thoughtworks.org/display/CCNET/Web+Dashboard

The CCTray an optional utility for use with the CruiseControl.NET Continuous Integration server. It provides feedback upon build progress, and allows control over some of the server’s operations.  As “optional utility” implies, the CCTray isn’t included with the base installation.  You may download the Windows Tray Application from the following location and install using provided default selection: http://downloads.sourceforge.net/ccnet/CruiseControl.NET-CCTray-1.3-Setup.exe?modtime=1182463076&big_mirror=0. There isn’t much to using the CCTray but I do consider it a handy add-on.  Basically, you will find a shortcut to the CCTray on your desktop. Launch the application to begin monitoring the automated build(s) status and schedule.  The information provides via the CCTray is very similar to that which is provided in the Dashboard application.  For more information on CCTray: http://confluence.public.thoughtworks.org/display/CCNET/CCTray.

 

Sample Configuration Files

If you have made it this far, I really, really hope your required configuration is similar to mine.  If so, this is your golden ticket.  I have provided two flavors of configuration files below. You will undoubted need to tweak the files a bit (replace the project locations and names along with credentials will obviously be required), but hopefully these will help get you started.  By the way, the formatting of the XML leaves something to be desires as I have formatted to fit within the given real estate.

 

Windows Service, StarTeam, MSBuild, Emailing and Unit Tests

<cruisecontrol>
  <project name=SampleService queue=Q1 queuePriority=0>
    <workingDirectory>c:\Projects\Services\SampleService
      </workingDirectory>
    <artifactDirectory>c:\ccnet\artifacts\Services\SampleService
      </artifactDirectory>
    <category>Continuous Builds</category>
    <webURL>http://buildbox.com/ccnet/server/local/project/
      SampleService/ViewProjectReport.aspx</webURL>
    <triggers>
      <intervalTrigger name=continuous seconds=600
                       buildCondition=IfModificationExists/>
    </triggers>
    <state type=state directory=c:\ccnet\state/>
    <labeller type=defaultlabeller>
      <prefix>Build-1.0.</prefix>
      <incrementOnFailure>false</incrementOnFailure>
    </labeller>
    <sourcecontrol type=starteam>
      <executable>c:\Program Files\Borland\StarTeam 2005\
        stcmd.exe</executable>
      <project>Project</project>
      <username>user</username>
      <password>password</password>
      <host>10.1.10.100</host>
      <port>11111</port>
      <path>Projects/Services/SampleService</path>
      <autoGetSource>true</autoGetSource>
    </sourcecontrol>
    <tasks>
      <msbuild>
        <executable>C:\WINDOWS\Microsoft.NET\Framework\
          v2.0.50727\MSBuild.exe
        </executable>
        <workingDirectory>C:\Projects\Services\SampleService
          </workingDirectory>
        <projectFile>SampleService.sln</projectFile>
        <buildArgs>/noconsolelogger /p:Configuration=Debug /v:m
          </buildArgs>
        <targets>Clean;Build</targets>
        <timeout>600</timeout>
        <logger>ThoughtWorks.CruiseControl.MsBuild.XMLLogger,
          C:\Program Files\CruiseControl.NET\server\
          ThoughtWorks.CruiseControl.MsBuild.dll</logger>
      </msbuild>
      <msbuild>
        <executable>C:\WINDOWS\Microsoft.NET\Framework\
          v2.0.50727\MSBuild.exe</executable>
        <workingDirectory>C:\Projects\Services\SampleService
          </workingDirectory>
        <projectFile>UnitTestBuild.proj</projectFile>
        <buildArgs>/noconsolelogger /p:Configuration=Debug /v:diag
          </buildArgs>
        <targets>Tests</targets>
        <timeout>1500</timeout>
        <logger>ThoughtWorks.CruiseControl.MsBuild.XMLLogger,
          C:\Program Files\CruiseControl.NET\server\
          ThoughtWorks.CruiseControl.MsBuild.dll</logger>
      </msbuild>
    </tasks>
    <publishers>
      <merge>
        <files>
          <file>c:\ccnet\artifacts\Services\SampleService\
            mbUnit-testResults.xml</file>
          <file>c:\ccnet\artifacts\Services\SampleService\
            msbuild-results.xml</file>
        </files>
      </merge>
      <xmllogger/>
      <email from=no-reply@buildbox.com
              mailhost=localhost includeDetails=TRUE>
        <users>
          <user name=User 1 group=BuildMaster
                  address=user.one@buildbox.com/>
          <user name=user 2 group=TechLead
                  address=user.two@buildbox.com/>
        </users>
        <groups>
          <group name=BuildMaster notification=always/>
          <group name=TechLead notification=change/>
        </groups>
      </email>
    </publishers>
  </project>
</cruisecontrol>

Web Application, StarTeam, MSBuild, Emailing, and Build Dependencies

<cruisecontrol>
  <project name=SampleWeb queue=Q2
           queuePriority=1>
    <workingDirectory>c:\Projects\Web\SampleWeb
      </workingDirectory>
    <artifactDirectory>c:\ccnet\artifacts\Web\SampleWeb
      </artifactDirectory>
    <category>Continuous Builds</category>
    <webURL>http://buildbox.com/ccnet/server/local/project/
      SampleWeb/ViewProjectReport.aspx</webURL>
    <modificationDelaySeconds>15</modificationDelaySeconds>
    <triggers>
      <intervalTrigger name=continuous seconds=3600
                       buildCondition=IfModificationExists/>
      <projectTrigger project=SampleData>
        <!– Force build when data component is successfully built. –>
        <triggerStatus>Success</triggerStatus>
        <innerTrigger type=intervalTrigger seconds=30
                      buildCondition=ForceBuild/>
      </projectTrigger>
    </triggers>
    <state type=state directory=c:\ccnet\state />
    <labeller type=defaultlabeller>
      <prefix>SampleWeb-Build-</prefix>
      <incrementOnFailure>true</incrementOnFailure>
    </labeller>
    <sourcecontrol type=starteam>
      <executable>C:\Program Files\Borland\
        StarTeam Cross-Platform Client 2006 R2\stcmd.exe</executable>
      <project>Project</project>
      <username>user</username>
      <password>password</password>
      <host>10.1.10.100</host>
      <port>11111</port>
      <path>Projects/Web/SampleWeb</path>
      <autoGetSource>true</autoGetSource>
    </sourcecontrol>
    <tasks>
      <msbuild>
        <executable>C:\WINDOWS\Microsoft.NET\Framework\
          v2.0.50727\MSBuild.exe</executable>
        <workingDirectory>C:\Projects\Web\SampleWeb
          </workingDirectory>
        <projectFile>SampleWeb.sln</projectFile>
        <timeout>900</timeout>
        <buildArgs>/clp:PerformanceSummary /verbosity:d
          </buildArgs>
        <logger>C:\Program Files\CruiseControl.NET\server\
          ThoughtWorks.CruiseControl.MsBuild.dll</logger>
      </msbuild>
    </tasks>
    <publishers>
      <xmllogger />
      <buildpublisher>
        <publishDir>c:\ccnet\artifacts\Web\SampleWeb</publishDir>
        <useLabelSubDirectory>true</useLabelSubDirectory>
      </buildpublisher>
      <email from=no.reply@buildbox.com
              mailhost=localhost includeDetails=TRUE>
        <users>
          <user name=User 1 group=BuildMaster
                  address=user.one@buildbox.com/>
          <user name=user 2 group=TechLead
                address=user.two@buildbox.com/>
        </users>
        <groups>
          <group name=BuildMaster notification=always/>
          <group name=TechLead notification=change/>
        </groups>
      </email>
    </publishers>
    <externalLinks>
      <externalLink name=Sample Web
             url=http://buildbox.com/SampleWeb/default.aspx />
    </externalLinks>
  </project>
</cruisecontrol>


What’s the Best Way to Manage a Database Queue?

20

June

This question circulates through the office about once a year.  “Um, I have Windows service which processes requests which are queued in a database table.  Everything is working great, but I would like to add a second instance of the service and I need to know the best way to ensure the services don’t step on each other.  In other words, how do I mark each row as it is being processed so it won’t be picked up again?” 

Here are the ingredients in my current ideal solution:

  1. The table must include the following columns: a Unique Identifier such as RequestID, Status, LockedBy, and LockedTime.  I also like to include RequestedTime and CompletionTime columns as I’ve found that capturing these values help when/if system stats are required.
  2. The key is to lock the current request at the time of processing.  To do so, you should use a transaction and an update lock.
  3. I think it is important to pull requests off the queue in an intelligent order.  FIFO is the best bet under most circumstances.  In this case, the RequestedTime column becomes imperative (unless your RequestID is an auto-incrementing identity column and you are comfortable ordering on it.)
  4. Finally, I like to make sure no request is left behind.  Let’s say Service A locks a request for processing and then the application server crashes.  I want the lock to eventually expire so Service B can pick up the row and save the day.

Assuming new requests are added to the queue as ‘Pending’ , one may use a routine similar to the one below to safely pull the next request off the queue:

create procedure [GetNextItemInQueue]
(
    @LockedBy varchar(75)
)
as 

set nocount on 

declare
    @Error                     int,
    @ExitCode                  int,
    @RowCount                  int,
    @PendingStatus               int,
    @MinutesBeforeBreakLock       int,
    @RequestId                   int

select
    @Error                      = 0,
    @ExitCode                   = 0,
    @RowCount                   = 0,
    @PendingStatus                = 1,
    @MinutesBeforeBreakLock        = 30 

begin tran ProcessQueue 

select top 1 @RequestId = RequestId
from Queue with (updlock)
where Status = @PendingStatus
and (LockedTime IS NULL OR
DateDiff(mi, LockedTime, GetDate()) >= @MinutesBeforeBreakLock)
order by RequestedTime Asc 

select @RowCount = @@RowCount, @Error = @@Error 

if (@RowCount = 0)
    goto ExitProc 

if (@Error <> 0)
    goto ErrorHandler 

update Queue
set    LockedBy = @LockedBy,
    LockedTime = GetDate()
where RequestId = @RequestId 

select @Error = @@Error
if (@Error <> 0)
    goto ErrorHandler 

select @RequestId as RequestId 

select @Error = @@Error
if (@Error <> 0)
    goto ErrorHandler 

goto ExitProc 

ErrorHandler:
    raiserror   (@Error, 16, 1)
    if @@trancount > 0
        rollback tran ProcessQueue
    select      @ExitCode =  -100 

ExitProc:
    commit tran ProcessQueue
    return      (@ExitCode)

Once the request is processed, a database call will be made to update the row’s Status to ‘Complete’ and CompletionTime. 

Note, you may choose to change the status of each row from ‘Pending’ to ‘Processing’ when you do the initial lock.  This is perfectly fine, but I’ve found that is unnecessary as the Processing status can be determined based on the value of other columns.

Again, that’s my current preferred implementation.  Perhaps there are better solutions out there?


RoundToInterval()

22

January

Disclaimer: If I were a betting man, I would bet there’s some code which does the following kicking around a forum, blog or two already.  Simply, I wasn’t lucky enough to find it.  Hence, the IF in “if I were a betting man.”

I ran into a case today where I wanted to reduce unused space in a generated chart.  I figured I could tighten up the chart if the minimum and maximum Y values were slightly beyond the min and max data point values.  For example, if the largest value in my dataset is 14, I would make the maximum value, and the corresponding Y axis label, 16. 

This fixed the real estate issue, but I didn’t like the my labels were now somewhat random and no longer looked clean.  For example, 14.64 + 2 = ugly. To get around this, I opted to pad the values to the nearest interval of 5 and helps with perceived consistency.

Original Chart


RoundToInterval() Function

Note the IntervalRounding enum below.  I consider it similar to Math.Round’s MidpointRounding enumeration where you can use MidPointRounding.ToEven to round a positive value to the nearest even number and MidPointRounding.AwayFromZero to round a value to the nearest number away from zero.  

// Sample call: Rounds 17 to 20. decimal d = RoundToInterval(17M, 5M, IntervalRounding.Up); private static decimal RoundToInterval(decimal value, decimal interval, IntervalRounding rounding) { // Guard against the divide by zero exception if (interval == 0) return decimal.MinValue; // For example, round 18 to the nearest interval of 5 // Quotient = 3.6, Remainder = 0.6, Whole = 3 decimal quotient = decimal.Divide(value, interval); decimal remainder = decimal.Subtract(quotient, decimal.Floor(quotient)); decimal whole = decimal.Subtract(quotient, remainder); // Rounding to the nearest interval and remainder // is over half way to the next interval value if (rounding == IntervalRounding.Nearest && remainder >= .5M || // Or we force rounding to the next highest interval // and we have a remainder. In otherwords, our value // isn’t on an interval already rounding == IntervalRounding.Up && remainder > 0M) { return interval * (whole + 1); } else { return interval * whole; } } public enum IntervalRounding { Nearest = 0, Up = 1, Down = 2 }

Final Chart:

The chart is now using more available space and the Y axis labels are rounded to the provided interval.  

Sorry for the blurry image.  If you really squint you can read it.  Maybe…


Getting Started with SubVersion

19

January

Over the past decade, I’ve primarily used two version control products: Visual Source Safe and Borland StarTeam.  I was “forced” into using StarTeam about 5 years ago, but having used it day-in and day-out for so long, I’ve come to recognize it as the superior of the two products (hands down.)  Through blogs and open source projects, SubVersion has gained my attention and I’m now giving it a whirl as well.  Having just installed it and TortoiseSVN (both open source) on my laptop, I thought I would walk you through my steps:

SubVersion

The goal of the Subversion project is to build a version control system that is a compelling replacement for CVS in the open source community. For a full list of features, visit the site: http://subversion.tigris.org/

For Windows Installations, you may find the latest installer (svn-1.4.2-setup.exe) here - http://subversion.tigris.org/servlets/ProjectDocumentList?folderID=91. A list of other packages are here.

Just follow the installer steps.  The installation is quick and painless…

TortoiseSVN

Tortoise is a  SubVersion client, implemented as a windows shell extension. As stated on their site:

TortoiseSVN is a really easy to use Revision control / version control / source control software for Windows. Since it’s not an integration for a specific IDE you can use it with whatever development tools you like.  TortoiseSVN is free to use. You don’t need to get a loan or pay a full years salary to use it.

The image below gives you an idea of what Tortoise has to offer.  To learn more, visit http://tortoisesvn.tigris.org/

 

 

 

 

The latest TortoiseSVN installer (TortoiseSVN-1.4.1.7992-win32-svn-1.4.2.ms