<?xml version="1.0" encoding="ISO-8859-1"?>
<rss version="2.0">
	<channel>
		<title>Technology Consulting</title>
		<link>http://www.tedspence.com/index.php</link>
		<description><![CDATA[No Footer]]></description>
		<copyright>Copyright 2010, Ted Spence</copyright>
		<managingEditor>Ted Spence</managingEditor>
		<language>en-US</language>
		<generator>SPHPBLOG 0.4.8</generator>
		<item>
			<title>Basic Security Settings for ASMX</title>
			<link>http://www.tedspence.com/index.php?entry=entry100812-131851</link>
			<description><![CDATA[I often find that Microsoft&#039;s older SOAP service pattern is quicker to set up and provides better performance than a more complex WCF service.  Here are a few basic settings you should use when publishing an ASMX service.<br /><br /><h5>Have an API folder</h5><br />Use a completely separate folder for all your ASMX files.  Don&#039;t keep them in a directory shared with regular web pages.  That way you can create more reliable monitoring, better firewall rules, or authentication.<br /><br /><h5>SSL</h5><br />Encrypting your web communication is totally worth it for any data of value.  It&#039;s easy, it doesn&#039;t have to cost you money (you can self-generate a certificate), and it gives you a box to tick on your security checklist.<br /><br />In IIS 7, click on the folder that holds your ASMX files and select &quot;SSL Settings.&quot;  Click &quot;Open Feature&quot;, and click &quot;Require SSL.&quot;  <br /><br /><h5>Hide Documentation</h5><br />You should probably not expose documentation for your API if you can avoid it.  Create a blank page that says &quot;Contact your account manager for documentation&quot; and use this walkthrough to configure it:<br /><br /><a href="http://www.15seconds.com/issue/040609.htm" target="_blank" >http://www.15seconds.com/issue/040609.htm</a>]]></description>
			<category></category>
			<guid isPermaLink="true">http://www.tedspence.com/index.php?entry=entry100812-131851</guid>
			<author>Ted Spence</author>
			<pubDate>Thu, 12 Aug 2010 17:18:51 GMT</pubDate>
		</item>
		<item>
			<title>Temp tables vs Declare Tables</title>
			<link>http://www.tedspence.com/index.php?entry=entry100804-140157</link>
			<description><![CDATA[SQL Server offers two different ways to create a temporary table in your stored procedure.  The two options are declaring a local variable and creating a temporary table.<br /><br /><pre>DECLARE @test TABLE (n int, v int, etc int)</pre><br /><br /><pre>CREATE TABLE #test (n int, v int, etc int)</pre><br /><br />When you create a local variable, you don&#039;t have to worry about deleting it when you are done.  Local variables have a large number of special cases that you should know about; you can pass them as parameters and use them in user defined functions.  But, they are created in memory, which doesn&#039;t always result in the performance you want.<br /><br />Temporary tables are generally better supported and have fewer special cases, but if you forget to delete them they can prevent you from running the same commands twice.<br /><br />Our database server has a dedicated disk for TempDB.  As a result, I discovered during some testing today that creating temporary tables was an order of magnitude faster than using declared tables.  I don&#039;t believe this performance issue is global, but in our case it was big enough.<br /><br />Now you know!]]></description>
			<category></category>
			<guid isPermaLink="true">http://www.tedspence.com/index.php?entry=entry100804-140157</guid>
			<author>Ted Spence</author>
			<pubDate>Wed, 04 Aug 2010 18:01:57 GMT</pubDate>
		</item>
		<item>
			<title>&quot;Workbridge Associates&quot; Scam</title>
			<link>http://www.tedspence.com/index.php?entry=entry100720-133728</link>
			<description><![CDATA[In my junior years, I accidentally sent my resume to a recruiting company known then as &quot;MacArthur Associates&quot;.  They turned out to be a shamelessly harassing, unprofessional and rude  headhunting company who inappropriately sent my resume to a number of companies and jeopardized job interviews I had already scheduled with people in my network by claiming that hiring me would trigger their referral fee, when I had already submitted my resume separately.  Their high pressure sales tactics alienated companies with whom I might have had a chance during a regular interview.  Eventually, I declined to work with them and moved on with my career.<br /><br />However, they subsequently renamed themselves &quot;Atlantis Partners&quot; and later to &quot;Workbridge Associates&quot;.  I&#039;ve heard they have more names as well.  Each individual group has the same churn rate - they hire people to man the phone banks to harass employers with nonstop phonecalls and resume blasts.  The recruiters do not know anything about the profession and know less about each individual company they target.  Instead, they get their leads through trolling Craigslist, Monster.com and other job boards and attempt to sell overpriced recruiting.<br /><br />I&#039;ve recently been made a target as an employer as well.  When I informed them that I would not work with them and asked them politely not to call me, they instead subjected me to random phonecalls from different salespeople from different affiliates all trying to get an &quot;in&quot;.  It truly leaves a bad taste.  There are so many good, honest, patient, and knowledgeable recruiters in the world, why work with someone like Workbridge?<br />]]></description>
			<category></category>
			<guid isPermaLink="true">http://www.tedspence.com/index.php?entry=entry100720-133728</guid>
			<author>Ted Spence</author>
			<pubDate>Tue, 20 Jul 2010 17:37:28 GMT</pubDate>
		</item>
		<item>
			<title>Select biggest record using LINQ</title>
			<link>http://www.tedspence.com/index.php?entry=entry100527-174759</link>
			<description><![CDATA[I just realized I&#039;d hate to write out the code to sort through a list of strings and find the longest one.  So I tried to do it in LINQ.  Here&#039;s what I got:<br /><br /><pre>List&lt;string&gt; words = new List&lt;string&gt;();<br />... fill in a bunch of data ...<br />var v = (from s in words orderby s.Length descending select s).Take(1);</pre><br /><br />The &quot;orderby s.Length&quot; clause sorts by string length.  Descending takes longest ones first and shortest ones last.  The part I didn&#039;t know was the &quot;.Take(1)&quot; function - that&#039;s interesting!  So, basically, I get the first string returned by the select statement.<br /><br />Of course, now you have to take a &quot;var&quot; and turn it into a string object.  Here&#039;s a shortcut:<br /><pre>IEnumerable&lt;string&gt; list = v as IEnumerable&lt;string&gt;;<br />if (list != null) {<br />    foreach (string s in list) {<br />        return s;<br />    }<br />}<br /></pre><br />]]></description>
			<category></category>
			<guid isPermaLink="true">http://www.tedspence.com/index.php?entry=entry100527-174759</guid>
			<author>Ted Spence</author>
			<pubDate>Thu, 27 May 2010 21:47:59 GMT</pubDate>
		</item>
		<item>
			<title>Windows 7 Problems</title>
			<link>http://www.tedspence.com/index.php?entry=entry090929-093615</link>
			<description><![CDATA[As much as I&#039;d welcome an upgrade to Windows, I doubt I&#039;ll be interested in what Microsoft is producing.<br /><br />1) The new UI has a nifty &quot;translucent&quot; look to everything.  However, this makes it virtually impossible to glance at text - now you have to study it carefully to make sure the background isn&#039;t distorting your words.  This is part of the reason I haven&#039;t grown attached to Vista, and I&#039;m sad to see it isn&#039;t changing much in the next release.<br /><br />2) Every individual part of the default UI is now more cluttered.  What used to be a simple &quot;folder&quot; window now has tons of options, hyperlinks, and buttons.  In many cases, the icons now randomly jump in size depending on what Windows thinks you have in the folder - you can go from tiny icons to huge ones, and funny &quot;1-5 star&quot; ratings systems interjected if the folder contains some music or video files.<br /><br />3) The control panel now attempts to hide stuff it thinks you don&#039;t want to see.  This causes two problems: first, it&#039;s harder to find the correct choice to make since you have to bypass all the choices it thinks you want to see; and secondly, when you make a choice, it&#039;s hard to know if you&#039;ve found the correct place to make it.  For example, It takes a very long time now to find &quot;network connections&quot;, when it used to be very simple to bring up a list of all network adapters on your computer and select the one you wanted to check.<br /><br />I would be very interested in an OS that would reduce the amount of unnecessary garbage that clogs up the user interface.  Ubuntu and Mac OSX both do well in this regard.]]></description>
			<category></category>
			<guid isPermaLink="true">http://www.tedspence.com/index.php?entry=entry090929-093615</guid>
			<author>Ted Spence</author>
			<pubDate>Tue, 29 Sep 2009 16:36:15 GMT</pubDate>
		</item>
		<item>
			<title>Null Pointer Exceptions when Comparing Strings</title>
			<link>http://www.tedspence.com/index.php?entry=entry090806-093246</link>
			<description><![CDATA[In the olden days, it was possible to compare strings like so:<br /><pre>string s1, s2;<br /><br />if (s1 = s2) then begin<br />    PrintLn(&quot;Strings match!&quot;);<br />end else begin<br />    PrintLn(&quot;Strings don&#039;t match.&quot;);<br />end;<br /></pre><br />Of course, we&#039;re no longer in the Turbo Pascal era.  What happens when you compare two strings using the == symbol in .net?  It wraps a call to String.Equals.  However, it also returns true if both strings are null!<br /><pre>string s1, s2;<br /><br />if (s1 == s2) {<br />    Console.WriteLine(&quot;S1 and S2 are equivalent according to String.Equals().  They could be null!&quot;);<br />} else {<br />    Console.WriteLine(&quot;S1 and S2 have different values, or one is null and the other is not null.&quot;);<br />}<br /></pre><br />Another way to run this test is to use s1.equals(s2) to see if they match - but wait!  Null pointer exceptions!<br /><pre>string s1 = null;<br />string s2 = &quot;Test&quot;;<br /><br />if (s1.Equals(s2)) {<br />    Console.WriteLine(&quot;Poof!  Null Pointer Exception!&quot;);<br />}<br /></pre><br />Of course, there&#039;s a simple answer.  There is one and only one correct way to compare strings.  Here it is:<br /><pre>string.Equals(s1, s2, StringComparison.CurrentCultureIgnoreCase)</pre><br />That also gets rid of the &quot;case sensitivity&quot; bug :)]]></description>
			<category></category>
			<guid isPermaLink="true">http://www.tedspence.com/index.php?entry=entry090806-093246</guid>
			<author>Ted Spence</author>
			<pubDate>Thu, 06 Aug 2009 16:32:46 GMT</pubDate>
		</item>
		<item>
			<title>SUM() Strings in Microsoft SQL server</title>
			<link>http://www.tedspence.com/index.php?entry=entry090724-095142</link>
			<description><![CDATA[It&#039;s really easy to sum numeric values.  You write this:<br /><pre>SELECT SUM(revenue) FROM orders</pre><br />Every so often I wish I could write a similar query for strings.  For example, wouldn&#039;t it be neat if we could get a list of employees by department like this?<br /><pre>SELECT TO_LIST(name), department FROM employees GROUP BY department</pre><br />As it turns out, there&#039;s a way to do this, but it&#039;s a little bit tricky.  You have to use an XML feature to do something it wasn&#039;t really designed to do.  By converting results into XML and back, you can produce lists exactly the way you might want.  Here&#039;s what the query looks like:<br /><pre>SELECT department<br />, SUBSTRING(<br />	(SELECT &#039;, &#039; + name<br />	FROM employees e2<br />    WHERE e2.department = e1.department<br />    FOR XML PATH(&#039;&#039;))<br />  , 2, 999) AS list_of_names<br />FROM employees e1<br />GROUP BY department</pre><br />Why does this work?  First, you&#039;re doing a main query that just lists all the departments.  So you get a list like this:<br /><pre>department<br />-----------<br />Sales<br />Accounting<br />Development</pre><br />Then, for each record in this list, we do a subquery listing &quot;&#039;, &#039; + name&quot; for each record.  That produces these results internally:<br /><pre>field1<br />-----------<br />, Bob<br />, Chris<br />, Alex</pre><br />The XML path with a blank parameter turns that into a gigantic text string, like so:<br /><pre>field1<br />------------------<br />, Bob, Chris, Alex</pre><br />And of course the Substring removes the first two characters.  The end result is that you get exactly the list you expect!]]></description>
			<category></category>
			<guid isPermaLink="true">http://www.tedspence.com/index.php?entry=entry090724-095142</guid>
			<author>Ted Spence</author>
			<pubDate>Fri, 24 Jul 2009 16:51:42 GMT</pubDate>
		</item>
		<item>
			<title>Fast Record Insertion into Microsoft SQL Server using C#</title>
			<link>http://www.tedspence.com/index.php?entry=entry090701-114812</link>
			<description><![CDATA[So I typically have to insert large data sets into our database.  I partner with a large number of companies that send us dozens of megabytes of data daily up to gigabytes.  Most of this data also needs parsing: we need to execute logic on each record before it gets pumped into the database.  This prevents us from using bulk data loaders except in the most basic cases.<br /><br />I&#039;m aware of a few different ways of loading data into a SQL database; each of which has positives and negatives.<br /><br /><h4>Insert using SqlCommand and SqlParameter</h4><br />This is typically the slowest approach, but it works well, it&#039;s safe from SQL injection attacks, and it can be used for any number of rows from one to one million.  Here&#039;s a sample insert for 10,000 rows:<br /><br /><pre>SqlConnection conn = new SqlConnection(connection_string);<br />conn.Open();<br />SqlCommand cmd = new SqlCommand(&quot;INSERT INTO test_table (var1, var2, var3, str1, str2) VALUES (@var1, @var2, @var3, @str1, @str2)&quot;, conn);<br />cmd.Parameters.Add(new SqlParameter(&quot;@var1&quot;, var1));<br />cmd.Parameters.Add(new SqlParameter(&quot;@var2&quot;, var2));<br />cmd.Parameters.Add(new SqlParameter(&quot;@var3&quot;, var3));<br />cmd.Parameters.Add(new SqlParameter(&quot;@str1&quot;, str1));<br />cmd.Parameters.Add(new SqlParameter(&quot;@str2&quot;, str2));<br />try {<br />    for (int i = 0; i &lt; 10000; i++) {<br />        cmd.Parameters[&quot;@var1&quot;].Value = var1;<br />        cmd.Parameters[&quot;@var2&quot;].Value = var2;<br />        cmd.Parameters[&quot;@var3&quot;].Value = var3;<br />        cmd.Parameters[&quot;@str1&quot;].Value = str1;<br />        cmd.Parameters[&quot;@str2&quot;].Value = str2;<br /><br />        cmd.ExecuteNonQuery();<br />    }<br />} finally {<br />    conn.Close();<br />}<br /></pre><br /><br /><h4>Insert using Stored Procedures</h4><br />This is a bit faster than the basic method, since the SQL server doesn&#039;t have to recompile a command each time it gets executed.  However, it&#039;s still fairly slow since each insert is a separate command.  <br /><br /><pre>SqlConnection conn = new SqlConnection(actual_string);<br />conn.Open();<br /><br />// Create the command string<br />SqlCommand cmd = new SqlCommand(&quot;EXEC insert_test @var1, @var2, @var3, @str1, @str2&quot;, conn);<br /><br />// Iterate through all of the objects<br />try {<br />    for (int i = 0; i &lt; 10000; i++) {<br />        cmd.Parameters.Clear();<br />        cmd.Parameters.Add(new SqlParameter(&quot;@var1&quot;, var1));<br />        cmd.Parameters.Add(new SqlParameter(&quot;@var2&quot;, var2));<br />        cmd.Parameters.Add(new SqlParameter(&quot;@var3&quot;, var3));<br />        cmd.Parameters.Add(new SqlParameter(&quot;@str1&quot;, str1));<br />        cmd.Parameters.Add(new SqlParameter(&quot;@str2&quot;, str2));<br /><br />        // Read in all the data<br />        cmd.ExecuteNonQuery();<br />    }<br />} finally {<br />    conn.Close();<br />}</pre><br /><br /><h4>Insert using Stored Procedure and Transactions</h4><br />This combines multiple best practices for SQL database management.  First, it uses parameters to protect against SQL injection attacks.  Next, it uses stored procedures, which isolates your database from your C# code and keeps the data layer separate from the business layer.  Third, it uses transactions to limit the length of time the database is hit by locking.  It&#039;s also faster!<br /><br /><pre>// Create the database connection<br />SqlConnection conn = new SqlConnection(actual_string);<br />conn.Open();<br />SqlTransaction trans = conn.BeginTransaction();<br /><br />// Create the command string<br />SqlCommand cmd = new SqlCommand(&quot;EXEC insert_test @var1, @var2, @var3, @str1, @str2&quot;, conn);<br />cmd.Transaction = trans;<br /><br />// Iterate through all of the objects<br />try {<br />    for (int i = 0; i &lt; 10000; i++) {<br />        cmd.Parameters.Clear();<br />        cmd.Parameters.Add(new SqlParameter(&quot;@var1&quot;, var1));<br />        cmd.Parameters.Add(new SqlParameter(&quot;@var2&quot;, var2));<br />        cmd.Parameters.Add(new SqlParameter(&quot;@var3&quot;, var3));<br />        cmd.Parameters.Add(new SqlParameter(&quot;@str1&quot;, str1));<br />        cmd.Parameters.Add(new SqlParameter(&quot;@str2&quot;, str2));<br /><br />        // Read in all the data<br />        cmd.ExecuteNonQuery();<br /><br />        // Periodically commit the transaction<br />        if (i % 1000 == 999) {<br />            trans.Commit();<br />            trans = conn.BeginTransaction();<br />            cmd.Transaction = trans;<br />        }<br />    }<br />} finally {<br />    if (trans != null) trans.Commit();<br />    conn.Close();<br />}</pre><br /><br /><h4>Insert using Union All</h4><br />This is a really clever approach that is blindingly fast, but it requires that you manually escape your text to prevent SQL injection attacks.  Here&#039;s some sample code:<br /><br /><pre>SqlConnection conn = new SqlConnection(actual_string);<br />conn.Open();<br /><br />// Build a massive insert statement<br />try {<br />    StringBuilder sql;<br />    for (int i = 0; i &lt; 100; i++) {<br />        sql = new StringBuilder();<br />        sql.Append(&quot;INSERT INTO test_table (var1, var2, var3, str1, str2) SELECT \r\n&quot;);<br />        for (int j = 0; j &lt; 100; j++) {<br />            sql.AppendFormat(&quot;{0}, {1}, {2}, &#039;{3}&#039;, &#039;{4}&#039; UNION ALL SELECT \r\n&quot;,<br />                var1, var2, var3, str1.Replace(&quot;&#039;&quot;, &quot;&#039;&#039;&quot;), str2.Replace(&quot;&#039;&quot;, &quot;&#039;&#039;&quot;)<br />            );<br />        }<br /><br />        // Subtract the last &quot;union all select&quot;<br />        sql.Length -= 19;<br /><br />        // Insert this batch<br />        SqlCommand cmd = new SqlCommand(sql.ToString(), conn);<br />        cmd.ExecuteNonQuery();<br />    }<br />} finally {<br />    conn.Close();<br />}</pre><br /><br /><h4>Insert using XML Parsing</h4><br />This is a clever trick I&#039;ve seen that seems to have roughly the same performance as a Union All, but can work with big XML documents.<br /><br /><pre>List&lt;string&gt; values = new List&lt;string&gt;();<br />for (int i = 0; i &lt; 10000; i++) {<br />    values.Add(String.Format(&quot;&lt;var1&gt;{0}&lt;/var1&gt;&lt;var2&gt;{1}&lt;/var2&gt;&lt;var3&gt;{2}&lt;/var3&gt;&lt;str1&gt;{3}&lt;/str1&gt;&lt;str2&gt;{4}&lt;/str2&gt;&quot;, var1, var2, var3, str1, str2));<br />}<br /><br />string xmlString = XMLUtilities.BuildSQLXmlString(&quot;Items&quot;, &quot;Item&quot;, values.ToArray());<br /><br />// Create the database connection<br />SqlConnection conn = new SqlConnection(actual_string);<br />conn.Open();<br />SqlCommand cmd = new SqlCommand(@&quot;  DECLARE @XML XML<br />                                    SELECT @XML = @xmlString<br />                                    DECLARE @Mapping TABLE (<br />                                        var1 INT,<br />                                        var2 INT,<br />                                        var3 INT,<br />                                        str1 NVARCHAR(255),<br />                                        str2 NVARCHAR(20)<br />                                    ) <br /><br />                                    INSERT INTO @Mapping(var1,var2,var3,str1,str2) <br />                                    SELECT ParamValues.Item.query(&#039;var1&#039;).value(&#039;.&#039;,&#039;INT&#039;)<br />                                           , ParamValues.Item.query(&#039;var2&#039;).value(&#039;.&#039;,&#039;INT&#039;)<br />                                           , ParamValues.Item.query(&#039;var3&#039;).value(&#039;.&#039;,&#039;INT&#039;)<br />                                           , ParamValues.Item.query(&#039;str1&#039;).value(&#039;.&#039;,&#039;NVARCHAR(255)&#039;)<br />                                           , ParamValues.Item.query(&#039;str2&#039;).value(&#039;.&#039;,&#039;NVARCHAR(20)&#039;)<br />                                      FROM @XML.nodes(&#039;/Items/Item&#039;) AS ParamValues(Item)<br /><br />                                    INSERT INTO test_table(var1,var2,var3,str1,str2)<br />                                    SELECT m.var1,m.var2,m.var3,m.str1,m.str2<br />                                      FROM @Mapping m&quot;, conn);<br />cmd.CommandTimeout = 10000;<br />cmd.Parameters.Add(new SqlParameter(&quot;@xmlString&quot;, xmlString));<br /><br />// Iterate through all the items we&#039;re going to create<br />try {<br />    cmd.ExecuteNonQuery();<br />} finally {<br />    conn.Close();<br />}</pre><br /><br /><h4>Relative Performance</h4><br />Bet you&#039;re wondering how they stack up!  Here are the numbers for a run of 10,000 records:<br /><br /><blockquote>Insert with Command/Parameter - 64.48 seconds<br />Insert Stored Procedure - 58.40 seconds<br />Insert Stored Procedure using BeginTransaction - 16.03 seconds<br />Insert Union All using SQLParameters - 3.18 seconds<br />Insert Union All using escaped text - 1.42 seconds<br />Insert XML - Occasional Crash!</blockquote><br /><br /><h4>Recommendations</h4><br />If you&#039;re writing general purpose code, just use the plain old stored procedure approach, and use a transaction if you do multiple inserts at a time.  It&#039;s guaranteed to be reliable, it&#039;s guaranteed to be a bit faster than the typical method, and it&#039;s all around good practice.<br /><br />However, if you do need absolute speed, I highly recommend the Union All approach.  The best part is, if you switch to a different database, most other RDBMS programs support something similar to Union All so your code will directly convert over!]]></description>
			<category></category>
			<guid isPermaLink="true">http://www.tedspence.com/index.php?entry=entry090701-114812</guid>
			<author>Ted Spence</author>
			<pubDate>Wed, 01 Jul 2009 18:48:12 GMT</pubDate>
		</item>
		<item>
			<title>CruiseControl.NET and Visual Studio - Quick Setup</title>
			<link>http://www.tedspence.com/index.php?entry=entry090611-095509</link>
			<description><![CDATA[Up until now, my team has been using Visual Studio for all our test and build needs.  We&#039;ve used Visual Studio completely - test suites, solution files, etc.  Recently we decided to start using CruiseControl.NET to put ourselves on the continuous integration path.  What&#039;s the best way to get started quickly on CruiseControl without changing our workflow?<br /><br />On the positive side, we use Subversion for our version control, which is supported right out of the box.  It&#039;s very easy for us to create a new account and flag it for use of CruiseControl.net, because we run VisualSVN Server and we have active directory authentication set up.<br /><br />Then the sacrifices came into play.  We decided against using NANT to build our code.  We have been successfully using Visual Studio 2008 to build / edit / test, and we didn&#039;t want to have to support both the Visual Studio style Solution / Project files (*.csproj, *.sln) as well as the NANT files.  So that solution didn&#039;t work.  Similarly, since we&#039;re using MSTEST (Microsoft&#039;s built-in test suite stuff), NUnit doesn&#039;t do everything we want.<br /><br />Shame on us for using proprietary vendor code :)  Anyways, turns out there is a good solution.  Here&#039;s how it works.<br /><br />1) Set up a desktop machine OR a Virtual PC image / VMWare image.  We went with the Virtual PC solution since I could just run it on my local desktop.  Microsoft publishes some stock <a href="http://www.microsoft.com/Downloads/details.aspx?FamilyID=21eabb90-958f-4b64-b5f1-73d0a413c8ef&amp;displaylang=en" target="_blank" >Virtual PC Images of Windows XP</a> that you can use.<br /><br />2) Install the following programs - Subversion client, Visual Studio 2008, and CruiseControl.NET.<br /><br />3) Create a new Subversion account on your repository named &quot;CruiseControl&quot;, and grant it read-only access.<br /><br />4) Edit your CCNet.config file (c:\program files\cruisecontrol.net\server\ccnet.config) to set up the project, the test solution, and launch CruiseControl as a service.  We chose to send out build emails since we&#039;re still adopting the service and we need to get people used to it; eventually I&#039;m sure folks will use the tray application.<br /><br />5) You should probably copy up the files from your CruiseControl folder to your intranet server in a location where everyone can browse it.  That helps you see statistics and build results.<br /><br />Here&#039;s my sample CCNet.config file:<br /><br /><pre>&lt;cruisecontrol xmlns:cb=&quot;urn:ccnet.config.builder&quot;&gt;<br />    &lt;queue name=&quot;Q1&quot; duplicates=&quot;ApplyForceBuildsReplace&quot;/&gt;<br />    &lt;project name=&quot;DMT&quot; queue=&quot;Q1&quot; queuePriority=&quot;1&quot;&gt;<br />        &lt;webURL&gt;your intranet web server URL here&lt;/webURL&gt;<br />        &lt;workingDirectory&gt;a temporary directory here&lt;/workingDirectory&gt;<br />        &lt;artifactDirectory&gt;a different temporary directory here&lt;/artifactDirectory&gt;<br />        &lt;modificationDelaySeconds&gt;10&lt;/modificationDelaySeconds&gt;<br /><br />        &lt;triggers&gt;<br />            &lt;intervalTrigger seconds=&quot;60&quot; name=&quot;continuous&quot; /&gt;<br />        &lt;/triggers&gt;<br /><br />        &lt;sourcecontrol type=&quot;svn&quot;&gt;<br />            &lt;executable&gt;c:\program files\subversion\bin\svn.exe&lt;/executable&gt;<br />            &lt;trunkUrl&gt;your subversion repository location here&lt;/trunkUrl&gt;<br />            &lt;workingDirectory&gt;Where the code will go on your build machine&lt;/workingDirectory&gt;<br />            &lt;username&gt;cruisecontrol&lt;/username&gt;<br />            &lt;password&gt;your password here&lt;/password&gt;<br />            &lt;revert&gt;true&lt;/revert&gt;<br />            &lt;cleanUp&gt;true&lt;/cleanUp&gt;<br />            &lt;tagOnSuccess&gt;false&lt;/tagOnSuccess&gt;<br />        &lt;/sourcecontrol&gt;<br /><br />        &lt;tasks&gt;<br />            &lt;msbuild&gt;<br />                &lt;executable&gt;c:\windows\microsoft.net\framework\v3.5\msbuild.exe&lt;/executable&gt;<br />                &lt;workingDirectory&gt;The application directory here&lt;/workingDirectory&gt;<br />                &lt;projectFile&gt;The name of the solution file&lt;/projectFile&gt;<br />                &lt;targets&gt;build&lt;/targets&gt;<br />                &lt;timeout&gt;300&lt;/timeout&gt;<br />            &lt;/msbuild&gt;<br />            &lt;exec&gt;<br />                &lt;executable&gt;cmd.exe&lt;/executable&gt;<br />                &lt;buildArgs&gt;/c del /s/q c:\mstest-results.trx&lt;/buildArgs&gt;<br />                &lt;description&gt;This clears out the data from the previous MSTEST.EXE run.&lt;/description&gt;<br />            &lt;/exec&gt;<br />            &lt;exec&gt;<br />                &lt;executable&gt;C:\program files\microsoft visual studio 9.0\common7\ide\mstest.exe&lt;/executable&gt;<br />                &lt;buildArgs&gt;/testcontainer:name of your test DLL file here /resultsfile:c:\mstest-results.trx&lt;/buildArgs&gt;<br />                &lt;description&gt;Run unit tests for your application and save the results to c:\mstest-results.trx&lt;/description&gt;<br />            &lt;/exec&gt;<br />        &lt;/tasks&gt;<br /><br />        &lt;publishers&gt;<br />            &lt;merge&gt;<br />                &lt;files&gt;<br />                    &lt;file&gt;c:\mstest-results.trx&lt;/file&gt;<br />                &lt;/files&gt;<br />            &lt;/merge&gt;<br />            &lt;xmllogger /&gt;<br />            &lt;statistics /&gt;<br />            &lt;modificationHistory  onlyLogWhenChangesFound=&quot;true&quot; /&gt;<br />            &lt;email from=&quot;fake send-from email address&quot; <br />                   mailhost=&quot;smtp server goes here&quot; <br />                   mailport=&quot;25&quot; <br />                   mailhostUsername=&quot;cruisecontrol&quot; <br />                   mailhostPassword=&quot;password goes here&quot; <br />                   includeDetails=&quot;true&quot;&gt;<br />                &lt;users&gt;<br />                    &lt;user name=&quot;Build List&quot; group=&quot;buildmaster&quot; address=&quot;your distribution list email address goes here&quot; /&gt;<br />                &lt;/users&gt;<br />                &lt;groups&gt;<br />                    &lt;group name=&quot;buildmaster&quot; notification=&quot;always&quot;/&gt;<br />                &lt;/groups&gt;<br />                &lt;modifierNotificationTypes&gt;<br />                    &lt;NotificationType&gt;Failed&lt;/NotificationType&gt;<br />                    &lt;NotificationType&gt;Fixed&lt;/NotificationType&gt;<br />                &lt;/modifierNotificationTypes&gt;<br />                &lt;subjectSettings&gt;<br />                    &lt;subject buildResult=&quot;StillBroken&quot; value=&quot;Build is still broken for {CCNetProject}&quot; /&gt;<br />                &lt;/subjectSettings&gt;<br />            &lt;/email&gt;<br />        &lt;/publishers&gt;<br />    &lt;/project&gt;<br />&lt;/cruisecontrol&gt;</pre>]]></description>
			<category></category>
			<guid isPermaLink="true">http://www.tedspence.com/index.php?entry=entry090611-095509</guid>
			<author>Ted Spence</author>
			<pubDate>Thu, 11 Jun 2009 16:55:09 GMT</pubDate>
		</item>
		<item>
			<title>Scheduling Tasks on Windows</title>
			<link>http://www.tedspence.com/index.php?entry=entry090421-101513</link>
			<description><![CDATA[In the old days I used to use the &quot;AT&quot; command to schedule simple recurring tasks for Windows.  The &quot;AT&quot; command was simple enough but had some limitations, and for whatever reason it never seemed reliable, although maybe that was because I never treated it seriously.<br /><br />As it turns out, Microsoft has replaced that with a new command called &quot;SCHTASKS&quot;.  Try this:<br /><br /><blockquote>Start | Run<br />CMD.EXE<br />schtasks /query</blockquote><br /><br />You should see a message like this:<br /><br /><blockquote>INFO: There are no scheduled tasks present in the system.</blockquote><br /><br />This just tells you that you do not have any scheduled tasks.  Apparently, though, schtasks lets you set them up via the command line!  Very useful.  Of course, the alternative is to go Start | Control Panel | Scheduled Tasks and use the GUI; however I find many situations where I like to have the ability to execute changes automatically using the command line.<br /><br />For more instructions on SCHTASKS, type &quot;SCHTASKS /?&quot; and you&#039;ll get a help page.  The instructions seem good enough, so enjoy!<br /><br />EDIT: Also note that when you&#039;re using SCHTASKS, quotes are escaped using the backslash.  Here&#039;s an example of an escaped nested command:<br /><br /><blockquote>schtasks /create /ru Administrator /rp 33d@r1294 /SC DAILY /TN &quot;Download Scripts&quot; /TR &quot;C:\GamePulse\TaskRun.exe \&quot;cmd.exe\&quot; \&quot;/c c:\gamepulse\running\download-scripts.cmd\&quot; log %CUSTID% \&quot;Download Scripts\&quot;&quot;</blockquote>]]></description>
			<category></category>
			<guid isPermaLink="true">http://www.tedspence.com/index.php?entry=entry090421-101513</guid>
			<author>Ted Spence</author>
			<pubDate>Tue, 21 Apr 2009 17:15:13 GMT</pubDate>
		</item>
	</channel>
</rss>
