SharePoint EventReceiver AfterProperties and Date fields

I had a brief to write an EventReceiver that looked for changes to certain fields and if the changes were detected to write an entry to another list. Seems simple enough but I ran into an issue with Date fields.

In the event receiver ItemUpdating event I was checking a Date field by first extracting the current date from the properties.ListItem[“MyDateField”] then checking this against the AfterProperties[“MyDateField”]. Of course the data stored in the AfterProperties[“MyDateField”] is not a Date but is a string.

I created a DateTime object from the AfterProperties[“MyDateField”] using the Convert.ToDateTime() method. All should have been fine but it turns out the two Date values were different – even though when I was doing my testing I was not changing the MyDateField, when I tested the two values for equality the comparison was returning false.

The reason for this was that my current time zone is BST (British Summer Time) and therefore the two date values were one hour out from each other. The solution was to take the DateTime from the Convert.ToDateTime() method and then get *another* DateTime by calling the DateTime.ToUniversalTime() method. This made sure that the DateTime I got from Convert.ToDateTime(AfterProperties[“MyDateField”]) was in same time zone as the ListItem[“MyDateField”] value.

This is down to how SharePoint internally stores DateTimes, one of the CAML attributes for a Field definition pertaining to Date fields is the StorageTZ attribute, some details here:

http://msdn.microsoft.com/en-us/library/ms437580(v=office.12).aspx

I suspect that this is the default for a Date field.

I can only imagine the havoc this type of code problem could reek if I had written this code in the depths of a December evening (as the time zone would be GMT which is a match for UTC), got everything working, passed testing and into production. Then, come the dawn of BST on the last Sunday of March the bug would appear.

It’s often the case that we developers outside the US complain about settings being defaulted to US based settings, this is one case where the default setting is the match for the UK, luckily for me it’s summer time and I picked up the issue.

A sore one indeed.

Posted in Uncategorized | Leave a comment

SPWeb.ProcessBatchData() doesn’t really take an xml string

If you look at the documentation for SPWeb.ProcessBatchData():

http://msdn.microsoft.com/en-us/library/microsoft.sharepoint.spweb.processbatchdata(v=office.12).aspx

You see that all you need to do is craft up a simple bit of xml and send to the method. Now I don’t know about you but whenever I create Xml strings in C# I usually use single quotes ‘ instead of double quotes ” for attribute values, this should of course not matter to any xml parser.

I was testing out the SPWeb.ProcessBatchData() method and wrote the following scratch console app:


namespace ScratchConsole
{
 class Program
 {
  static void Main(string[] args)
  {
   using(SPSite siteCollection = new SPSite("http://skpkapd0107:1001/"))
   using(SPWeb testSite = siteCollection.RootWeb)
   {
    SPList listToBatchUpdate = testSite.GetList("/Lists/ScratchTestList");

    StringBuilder sb = new StringBuilder();
    sb.Append("<?xml version='1.0' encoding='UTF-8'?><Batch OnError='Return'><Method ID='1'>");
    sb.AppendFormat("<SetList>{0}</SetList>", listToBatchUpdate.ID.ToString());
    sb.Append("<SetVar Name='ID'>New</SetVar><SetVar Name='Cmd'>Save</SetVar>");
    sb.Append("<SetVar Name='urn:schemas-microsoft-com:office:office#Title'>A New Item Created By SPWeb.ProcessBatchData()</SetVar>");
    sb.AppendFormat("<SetVar Name='urn:schemas-microsoft-com:office:office#ADateField'>{0}</SetVar>", SPUtility.CreateISO8601DateTimeFromSystemDateTime(DateTime.Now));
    sb.AppendFormat("<SetVar Name='urn:schemas-microsoft-com:office:office#ANumberField'>{0:d}</SetVar>", 3456);
    sb.Append("</Method></Batch>");
    string batchUpdateXml = sb.ToString();
    testSite.ProcessBatchData(batchUpdateXml);  
   }
  }
 }
}

Seems simple enough, I’m sending a single new item to a test list with a couple of extra columns (a DateTime and Number column). The problem was I was getting the following (very unhelpful) exception:

System.ArgumentException was unhandled
  Message=”Value does not fall within the expected range.”
  Source=”Microsoft.SharePoint.Library”
  StackTrace:
       at Microsoft.SharePoint.Library.SPRequestInternalClass.ProcessBatchData(String bstrUrl, String bstrData, ISPDataCallback pResultCallback)
       at Microsoft.SharePoint.Library.SPRequest.ProcessBatchData(String bstrUrl, String bstrData, ISPDataCallback pResultCallback)
       at Microsoft.SharePoint.SPWeb.ProcessBatchData(String strBatchData)
       at ScratchConsole.Program.Main(String[] args) in J:\code\Homechoice\spikes\rippeyc\ScratchConsoleSolution\ScratchConsole\Program.cs:line 37
       at System.AppDomain._nExecuteAssembly(Assembly assembly, String[] args)
       at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
       at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
       at System.Threading.ThreadHelper.ThreadStart()
  InnerException:

 Just no idea what the problem was supposed to be. After staring at the screen and the documentation for SPWeb.ProcessBatchData() I made a wee change to the code:


namespace ScratchConsole
{
 class Program
 {
  static void Main(string[] args)
  {
   using(SPSite siteCollection = new SPSite("http://skpkapd0107:1001/"))
   using(SPWeb testSite = siteCollection.RootWeb)
   {
    SPList listToBatchUpdate = testSite.GetList("/Lists/ScratchTestList");

    StringBuilder sb = new StringBuilder();
    sb.Append("<?xml version=\"1.0\" encoding=\"UTF-8\"?><Batch OnError=\"Return\"><Method ID=\"1\">");
    sb.AppendFormat("<SetList>{0}</SetList>", listToBatchUpdate.ID.ToString());
    sb.Append("<SetVar Name=\"ID\">New</SetVar><SetVar Name=\"Cmd\">Save</SetVar>");
    sb.Append("<SetVar Name=\"urn:schemas-microsoft-com:office:office#Title\">A New Item Created By SPWeb.ProcessBatchData()</SetVar>");
    sb.AppendFormat("<SetVar Name=\"urn:schemas-microsoft-com:office:office#ADateField\">{0}</SetVar>", SPUtility.CreateISO8601DateTimeFromSystemDateTime(DateTime.Now));
    sb.AppendFormat("<SetVar Name=\"urn:schemas-microsoft-com:office:office#ANumberField\">{0:d}</SetVar>", 3456);
    sb.Append("</Method></Batch>");
    string batchUpdateXml = sb.ToString();
    testSite.ProcessBatchData(batchUpdateXml);  
   }
  }
 }
}

And it worked, all I did was replace the single quote ‘ in the xml with a double quote “.

Looking back at the exception stack trace it makes you think that in the SPRequestInternalClass.ProcessBatchData() method there must be code that is parsing the string directly and not handing off the parsing to an xml parser. So the string you pass to the method isn’t really an xml string, it’s just a bit of text that resembles an xml string.

Posted in Uncategorized | 3 Comments

SPListItem.Versions – don’t forget the PermMask in your CAML

I’m working on a simple enough task to export data from a publishing pages library into an external database for analysis. The brief was to export the meta data about each page, including all published versions of the pages.

Seems simple enough and away I went with a simple CAML query of:

<Where><Eq><FieldRef Name='_ModerationStatus' /><Value Type='ModStat'>Approved</Value></Eq></Where>

Once I’d retrieved the list items and started iterating through the collection I then hit an exception on the following line:

SPListItemVersionCollection versions = item.Versions;

The exception details were:
Type:System.ArgumentException
Message:Value does not fall within the expected range.
Stack Trace:   at Microsoft.SharePoint.SPFieldMap.GetColumnNumber(String strFieldName)
   at Microsoft.SharePoint.SPListItemCollection.GetRawValue(String fieldname, Int32 iIndex)
   at Microsoft.SharePoint.SPListItem.GetValue(SPField fld, Int32 columnNumber, Boolean bRaw)
   at Microsoft.SharePoint.SPListItem.GetValue(String strName, Boolean bThrowException)
   at Microsoft.SharePoint.SPListItem.GetValue(String strName)
   at Microsoft.SharePoint.SPListItem.get_EffectiveBasePermissions()
   at Microsoft.SharePoint.SPListItem.DoesUserHavePermissions(SPBasePermissions permissionMask)
   at Microsoft.SharePoint.SPListItem.CheckPermissions(SPBasePermissions permissionMask)
   at Microsoft.SharePoint.SPListItem.get_Versions()

Now the bizarre thing was that if I set a breakpoint on the above line, then when paused on the breakpoint if I added a watch on item AND expanded the item in the watch window then the code would work from that point on (classis heisenbug).

I looked through the stack trace in reflector and found the answer – my SPQuery object had a small ViewFields property (I was retrieving just the fields I wanted to archive) and reflector showed that the code was throwing the ArgumentException because the SPListItem was missing the PermMask field. A quick change to my SPQuery.ViewFields so that it included a:

<FieldRef Name='PermMask' />

and everything worked fine, score one more to reflector.

I’d bet there’s many more dark corners in the SharePoint API that have a wee look at the PermMask field, might have to include it in any SPQuery.ViewFields value from now on.

Colin

Posted in SharePoint | Leave a comment

Redirect STDOUT and STDERR to the same place

I was deploying a SharePoint solution today and had put together a bunch of .cmd files for the operations team to use. So I sat with the team as they ran the scripts and lo and behold I had deployed the .wsp with the debug dll instead of the release .dll so some of my feature activation code broke (the debug code was looking for security groups that existed on my dev box).

Anyway, what I had done was give the team the instructions to run the .cmd files like so:

C:\DeploymentFolder\ActivateFeatures.cmd > ActivateFeaturesOutput.txt

When the operations person ran this I noticed that the error message appeared in the command prompt – hmmmmm. Of course the operations person just said “oh that’s because your STDOUT and STDERR are two different things, Unix you know”.

A quick search and I found that what I should have done was:

C:\DeploymentFolder\ActivateFeatures.cmd 1> ActivateFeaturesOutput.txt 2>&1

Courtesy of http://support.microsoft.com/kb/110930.

Posted in SharePoint | Tagged | Leave a comment

Save list as template – Include Content disabled: A solution

So today I was putting together a simple SharePoint 2007 site to manage the testing of an application I’m developing. The users of the app have no real experience in proper testing so I thought I’d help them out by creating a custom issues list that they could use to manage the list of test steps they had to carry out.

My goal was to create the custom list, populate the list with the series of test steps and then save the list as a template including the content. For each round of testing we’d then create an instance of this new list and the testers would execute each of the steps. Seemed simple enough.

I created and populated the list (removing some of the default issues list columns and adding a bunch of custom columns, irrelevant to this discussion), then I went to good old List Settings and clicked Save list as template, filled out the File name, Template name and Template description fields and was then about to select the Include Content check box when I noticed that the check box was disabled.

Hmmmmm.

After some searching I found that an Issues List does not allow content to be saved when saving as a template, I wanted to use an Issues List as I wanted the Send email when ownership is assigned setting to be available.

 More searching turned up the DontSaveInTemplate attribute of the List element in a list schema. (note that this also turned up the EmailAssignTo attribute which is apparently how the Issues List email setting works).

So here’s what I did to resolve this issue:

1. Save the list as a template without including the content.

2. Browsed to the List Template Gallery.

3. Downloaded the list template I saved in step 1.

4. Cracked open the .stp file using 7-Zip (of course).

5. Opened the manifest.xml file.

6. Navigated to the ListTemplate/UserLists/List element

7. Changed the DontSaveInTemplate=”TRUE” attribute to DontSaveInTemplate=”FALSE” (leaving the lovely EmailAssignTo=”TRUE” well and truly alone)

8. Saved the manifest.xml file

So, now I had a manifest.xml file containing a list template which in theory should allow me to create a list instance from, *then* be able to save a template from this new list instance which *will* allow me to Include Content. My next task was to get the manifest.xml file packaged back up into a .stp (.cab) file.

Now creating .cab files can be a PITA but on this occasion it was pretty simple. My SharePoint development machine has Visual Studio 2008 Professional installed (obviously) and as a result I had a copy of MakeCab.exe in the “C:\Program Files\Microsoft Visual Studio 9.0\SmartDevices\SDK\SDKTools” folder.

I copied the manifest.xml file to the aforementioned folder, fired up a command prompt, navigated to the folder and ran the following command:

MakeCab manifest.xml TestScriptTemplate.stp

Now I had a nice, shiny template that I could use to create a list, populate the list with the default set of test scripts *then* save this new list as a template which I could then use to create the test script issues list for each round of testing. 

Thinking about this made me realise that there are lots of occasions where you might want to grab a list template, crack it open, make a few small changes and then package it back up and upload the template back to the site. There’s been several times where I know that I could make a list view filter work using nested AND and OR conditions if I wrote the CAML filter by hand.

Using this method of downloading the template, cracking open the manifest.xml, doing a quick edit then packaging it back up again will let me just get on with the job without having to resort to building a .wsp and asking the awfully nice server administrator if they’d be so kind as to install my custom .wsp.

YMMV

Colin

Posted in Uncategorized | 3 Comments

A brave new world

And here was I thinking the other day: “Now I’ve joined twitter as @finarne I need to decide on a blogging platform to write up my SharePoint articles” and lo and behold, I check out http://techmeme.com tonight and the top story is Windows Live Spaces moving to WordPress.com – decision made for me, now all I need to do is actually write some articles.

Posted in Uncategorized | Leave a comment

TypeMock and ASP.NET

Unit Testing ASP.NET? ASP.NET unit testing has never been this easy.

Typemock is launching a new product for ASP.NET developers – the ASP.NET Bundle – and for the launch will be giving out FREE licenses to bloggers and their readers.

The ASP.NET Bundle is the ultimate ASP.NET unit testing solution, and offers both Typemock Isolator, a unit test tool and Ivonna, the Isolator add-on for ASP.NET unit testing, for a bargain price.

Typemock Isolator is a leading .NET unit testing tool (C# and VB.NET) for many ‘hard to test’ technologies such as SharePoint, ASP.NET, MVC, WCF, WPF, Silverlight and more. Note that for unit testing Silverlight there is an open source Isolator add-on called SilverUnit.

The first 60 bloggers who will blog this text in their blog and tell us about it, will get a Free Isolator ASP.NET Bundle license (Typemock Isolator + Ivonna). If you post this in an ASP.NET dedicated blog, you’ll get a license automatically (even if more than 60 submit) during the first week of this announcement.

Also 8 bloggers will get an additional 2 licenses (each) to give away to their readers / friends.

Go ahead, click the following link for more information on how to get your free license.

Posted in Computers and Internet | Leave a comment