In my previous post, I described the current state of event handlers in MOSS and WSS v3. Also, I listed the available event that can be handled by developers' custom code. In this post, I would like to vent, just a bit, on some of the challenges that I have faced implementing event handlers on a custom list.
My first challenge is concerning deployment. This is rather easily solved, by implementing a free tool to manage the connection between your event handler assembly and event(s) which it will handle. Deploying this bugger is a bit tedious, but at least it is well documented. For more, check Brian Wilson's posts. He does a good job of explaining how to write your first event handler.
Now, on to the meat of this post: my second challenge. (Let me apologize now if this is esoteric. I can't help myself.) I would like to talk about the SPItemEventDataCollection object.
In my particular case, I am using a synchronous event handler, so that I can abort the updates to the list item if they do not meet the business rules. I want to check each field in the list, determine which fields have values that have changed, compare them against the business rules and take some action.
When you override one of the event handling methods in the SPItemEventReceiver class, you are passed an SPItemEventProperties object. This object contains tons of useful information, such as the list item which fired the event (which in turn can get you to the list, the web or the site collection objects.) This object also has two very interesting members, AfterProperties and BeforeProperties, which are of the type SPItemEventDataCollection.
Your immediate inclination at this point might be to loop through the BeforeProperties collection and compare its values to the corersponding values in the AfterProperties collection, but you would be misguided. The BeforeProperties.count == SPListItem.Fields.Count, however the values for every field in BeforeProperties == null. I also tried the AfterProperties.ChangedProperties collection with similar results. Everything is null. After some research, I discovered that this class is only used in event tied to document libraries. Bummer.
All is not lost, however, since you can access the SPListItem object trough the SPItemEventProperties object. Now, I can just compare them, right? Almost. The values stored in the AfterPropereties collection are all stored as strings, compared to the SPListItem, which stores some its fields as SPField objects. Comparing their values is far from straightforward. I'll give you a couple of examples.
Single-valued Person lookup fields are stored as just their ID, where ID is a string representation of their integer User ID (ID field in the userinfo and userdata tables). Multi-valued Person lookup fields store the user information as ID;#domain\username;#ID;#domaing\username... (One other thing to note here: If you enter the user's email addres into a Person lookup field, the corresponding AfterProperties value will be null. Entering their name or user id works fine.)
DateTime fields are stored as GMT in AfterProperties, but as local time in SPListItem, so don't try to just DateTime.Parse((string)properties.AfterProperties["MyDateField"]), unless of course you live in Greenwich, England. Otherwise your values will be off (in my case they were off by 7 hours, since I am CST and have Daylight Savings).
All this being said, I am sure that it added less than 10% more code to my 600 line event handler. But they are things to watch out for, especially if you expecting the AfterProperties to store your list item changes as their respective SharePoint types (SPFieldDateTime, SPFieldUrl, et cetera).