PowerShell – Passing Arguments to a Function – Don’t Use Commas!

Recently picked up PowerShell for some automated deployment scripts – when passing more than one argument to a function I seemed to end up with one strange object, and any subsequent arguments were simply missing.
The problem was that within PowerShell you must separate arguments to a function by spaces rather than commas – if you use commas you are actually creating a list (of objects) and passing that as the first argument.

So say you have a function myFunction defined somewhere – the below call will dynamically create a list (the parentheses are simply ignored) and pass that to myFunction:

    myFunction($arg1, $arg2, $arg3);
    #myFunction will receive ONE argument from this call, a list with three elements
    #The second and third parameter will be null.

The correct way to pass three separate arguments is with spaces:

    myFunction $arg1 $arg2 $arg3;
    # my function will receive THREE separate arguments from this call. 

I mainly code in C# – and I think the main reason this caught me out, is that PowerShell scripting language is otherwise very similar to C#, especially when you code in a nice IDE like Power GUI.
At the time I calling a function inside a foreach loop with a XmlElement, and it’s just odd that you then have to “switch back” to this kind of classic command line passing of params.. but there you go – it’s a shell, albeit a powerful one!

This is also covered along with some other great PowerShell gotchas in the excellent article here http://www.johndcook.com/powershell_gotchas.html.

Digital Volcano’s Excellent Duplicate File Cleaner

Some times one is amazed at the free software out there – Digital Volcano’s Duplicate Cleaner is a brilliantly designed, super fast, easy to use piece of software brilliance, and it really is just free – no “you can only” or “x days only” limitation – just full featured and free.
Get your copy quick: http://www.digitalvolcano.co.uk/content/duplicate-cleaner

Umbraco Examine – Indexing Large Sites – ThreadAbort Timeout Error Workaround

This relates to a problem with Umbraco Examine indexing very large Umbraco sites. The issue is being looked at by the core Examine team, however if like us you can’t wait – the below describes a workaround we used:

As part of an Umbraco 4.7 (which ended up being a 4.7.1 upgrade for other reasons) we had an issue with the UmbracoExamine indexing timing out when initially creating the internal indexes for Members and Content respectively. This initial indexing action happens on the application start as the UmbracoEventManager constructor calls EnsureIndexesExist().
The item is logged in the work item here: http://examine.codeplex.com/workitem/10324 – and the good people at the Farm have already started working on a solution.
The current solution is to split the Lucene .add files into folders with 500 in each, which improves the IO performance. Another very welcome addition is a new RebuildOnAppStart configuration setting in the ExamineSettings.config file:

<Examine RebuildOnAppStart="false">


We used this setting in conjunction with the Examine Index admin package found here: http://our.umbraco.org/projects/backoffice-extensions/examine-index-admin – which allows you to kick off the indexSets one at a time, and monitor the progress.

Some other potential issues:

  • In a large site you might get >9 batch folders in the queue folder – currently this breaks (bear in mind we’re using untested source code here). Likely this will be fixed soon – but if you still get this, you need to add a Int32.Parse to the ordering statements in the LuceneIndexer.cs file.
  • The processing does not index all the queued batch folders, again you need to look at LuceneIndexer.cs, in the ForceProcessQueueItems() you need to order the names using an Int32.Parse and then convert this sort to a list before iterating.
  • The Examine Index package may time out before generating all the .add files (in the batched folders) as this is not done asynchronously. To solve this we added this to the package file at \usercontrols\packages\umbraco\ExamineIndexAdmin.ascx.cs (note this does not have to be compiled, uses the CodeFile attribute in the aspx):

    private int _orgTimeOut;
    
            private void Page_Init(object sender, System.EventArgs e)
            {
                _orgTimeOut = Server.ScriptTimeout;
                Server.ScriptTimeout = 3600; //1 hour (seconds)
            }
    
            private void Page_PreRender(object sender, System.EventArgs e)
            {
                Server.ScriptTimeout = _orgTimeOut;
            }
    

    This is adapted from here: http://codebetter.com/petervanooijen/2006/06/15/timeout-of-an-asp-net-page/.
    I'm not sure it's a super great solution, as it will affect all requests until the pre_render event reverts - but I wanted a solution that did not require us to modify the web.config.

All in all, the above is a viable interim solution for what is likely to be a temporary problem in the Umbraco Examine project. Once these indexes have been generated, the day-to-day indexing is unaffected by the size of your content (or number of members) and works perfectly.
As discussed, it also seems that the Umbraco Examine folks at The Farm are already aware of these issues, so I'm sure they will have an updated release that fixes this (properly) very soon.

Umbraco Upgrade hangs at 35% “Upgrading Database Tables”

This is an issue that seems to apply to the install wizard for Umbraco 4.6 and 4.7, when upgrading from older versions.
At the database step you get to 35% with the status “upgrading database tables” – and nothing else happens.

This is caused by a bug in the install code – the format of the code makes it hard to spot – but below reformatted section from \umbraco\presentation\install\utills\p.aspx.cs shows the problem – the first block is incorrect and should be removed:

Howver, at the point where you see the “35% Updatings database tables…” the code has already checked that’s it’s able to connect to and upgrade your database. The Install() call (above) is a quick set of SQL statements – only takes a couple of seconds to run – and the RefreshContent() is async (will run separately in another thread), so if you have been looking at the “35 upgrading tables” for a little while it’s pretty safe to assume that this has completed without errors, and hitting F5 will then force a recheck of your database which should send you on to the next step.

I’ve also covered this issue in the Umbraco forums here: http://our.umbraco.org/forum/getting-started/installing-umbraco/17531-Upgrade-to-461-Updating-database-tables-Problem

Loading Custom youTube player Using jQuery

Did this in ASP.NET – should roll into a user control, and load the jScript using the Client Dependency Framework – but code is as follows (jQuery from here):


	<div id="youtube-player"></div>
	$('#youtube-player').youTubeEmbed({
		video: 'http://www.youtube.com/watch?v=TUoOcDGMgT4'
		, width: 226 		// Height is calculated automatically
		, progressBar: false	// Hide the progress bar
		, autoplay: 1
	});
	

A new Session ID for each request (until you use session)

Caught me out twice now this – watch it with the Session.IsNewSession property – a new session ID is generated for each request in applications that do not store data in the session dictionary.
http://msdn.microsoft.com/en-us/library/aa479041.aspx

Application Timers (to replace scheduled tasks)

I do not like scheduled tasks.
They most often take the form of little bits of unversioned code (in console apps) that exist outside the main solution.
Moving these into the main application allows us to utilise the application’s logging utilities, adds versioning to the logic and configuration settings and ensures that the code is both updated and deployed along with the rest of the solution.

Below is some initial code – the idea is to have a singleton class that starts a set of timers. This can then be called from Application_Start or a similar global event.
Some improvements to the below would be to make the application timers use the provider pattern – so we can code up new timers when we need, and perhaps add a .config section to load these from.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Timers;
using System.Net;
using HobNobClubServices.Library;
 
namespace HobNobClubApps.Library
{
      /// 
      /// Singleton timer
      /// 
      public sealed class ApplicationTimers
      {
            private static readonly ApplicationTimers instance = new ApplicationTimers();
            private static bool timersInitialised = false;
 
            private ApplicationTimers() { }
 
            public static ApplicationTimers Instance
            {
                  get
                  {
                        return instance;
                  }
            }
 
            public void InitTimers()
            {
                  if (timersInitialised)
                        return;
 
                  InitHobNobInsuranceEmailAbandonedQuotesTimer();
 
                  timersInitialised = true;//Only do this once in the application:
            }
 
            private void InitHobNobInsuranceEmailAbandonedQuotesTimer()
            {                
                  Timer HobNobInsuranceEmailAbandonedQuotesTimer = new Timer();
                  int intervalInMinutes = Properties.Settings.Default.HobNobInsuranceEmailAbandonedQuotesTimerInterval;
                  HobNobInsuranceEmailAbandonedQuotesTimer.Interval = (intervalInMinutes * 60000); //Convert to millisecs
                  HobNobInsuranceEmailAbandonedQuotesTimer.Elapsed += new ElapsedEventHandler(HobNobInsuranceEmailAbandonedQuotesTimer_Elapsed);
                  HobNobInsuranceEmailAbandonedQuotesTimer.Start();
            }
 
            private void HobNobInsuranceEmailAbandonedQuotesTimer_Elapsed(object sender, ElapsedEventArgs e)
            {
                  string errorMsg = String.Empty;
                  try
                  {
                        int intervalInMinutes = Properties.Settings.Default.HobNobInsuranceEmailAbandonedQuotesTimerInterval;
                        //Set the delay in emailing people to the same as this timer interval (so if this timer runs every 30 mins, people will be emailed if they've left a quote alone for 30 mins):
                        string methodNameAndParams = String.Format("/EmailAbandonedQuickQuotes?delayInMinutes={0}", intervalInMinutes);
                        WebRequest webRequest = HttpWebRequest.Create(Properties.Settings.Default.HobNobClubApps_SLHobNobInsurance_HobNobInsurance + methodNameAndParams);
                        WebResponse webResponse = webRequest.GetResponse();                    
                  }
                  catch (Exception ex)
                  {
                        errorMsg = ex.Message;
                        ExceptionManager.LogExceptionEvent(ex, Logger.Source.HobNobInsurance);
                  }
 
                  Logger.WriteInfoLog(
                        String.Format("The HobNobInsuranceEmailAbandonedQuotesTimer ran at {0} {1}",
                                                DateTime.Now, String.IsNullOrEmpty(errorMsg) ? "with no errors (from the timer)." : "and failed with: " + errorMsg),
                              Logger.Source.HobNobInsurance);
            }
      }
}