Tuesday, August 01, 2006

Bad news - synchronous list events bug (or missing feature)


I have had a chance to look into this again now, and found that indeed it is possible to access and change properties in ItemAdding, if you are doing it through the "AfterProperties" hash table of the event, and not through the list item.
Some things to note:
During ItemAdding, there is no SPListItem object to refer to. It is null in the event properties, because to get an SPListItem a record for the item should be in the database, and during ItemAdding nothing has been written to the database yet.
After ItemAdding, the item is added to the database. When it is added, the values from the AfterProperties are applied to it. If you try to add a property to that hashtable that doesnt exist in the list definition, the user will get an error that the field isn't installed correctly. You may encounter this if you don't use the internal name of the field when you set the AfterProperties.
The moral of the story - Use the AfterProperties in ItemAdding, and when you use it to get or set a field, make sure you use the field's internal name.
That said, my argument with Sahil (see below in the original post) remains - you cannot use the SPListItem, because one cannot exist. His sample code modifies the item that was added before the current item, and not the current one.

Here is a sample code that modifies one of the fields in the ItemAdding event. In this example we have a links list, with a custom column called "Tool Tip" (internal name is "Tool_0x0020_Tip") and we want to set it automatically to the title of the link the user picked:

public override void ItemAdding(SPItemEventProperties properties)


            string toolTipFieldInternalName = "";

            using (SPWeb web = properties.OpenWeb())


                toolTipFieldInternalName = web.Lists[properties.ListId].Fields[TOOL_TIP_FIELD_NAME].InternalName;




            string urlVal = properties.AfterProperties["URL"].ToString();

            SPFieldUrlValue val = new SPFieldUrlValue(urlVal);

            string desc = val.Description;

            properties.AfterProperties[toolTipFieldInternalName] = desc;


Original post: (was posted during Beta2)

I have been trying to create an event handler that checks the item's properties before it is saved. This is supposed to be easy in MOSS - just create an event handler, override the "ItemAdding" event and check the properties of the list item. right?


Apperantly, the SPItemEventProperties object that is received in the ItemAdding event is totaly useless! The "ListItem" is null, the ListItemID is null, the AfterProperties and the BeforeProperties are both empty so basically we have no idea what the user typed - we have no way to control it!

Searching the internet I found that Sahil wrote a terrific article on creating an event handler (with really good funny writing and good guidlines) in which he tried to implement a workaround to the problem.

Unfortunately, I tested his code and it seems something is missing. I think he meant to use the ItemAdded event or something (or maybe not, since his scenario demands using the ItemAdding event).

Basically he is trying to get the item from the list by getting the last item int the list. But he didnt notice that the item doesnt exist yet, so his code is quering an existing item instead of the new one about to be added - he is always checking the last item in the list, but not the one the event is triggered for. So to sumarrize - I found no way currently to get the list item details before it is added to the list (then you can use the "ItemAdded" event). I have contacted Microsoft and waiting for their reply.

If you are using Sahil's article (which is quite good except for the above), look out for embedding the list's URL in the code. Sahil probably meant to write about it, but he focused on the demo.

The more elegant way to get to the list is:

Shail's original code SPList SahilSurvey = properties.OpenWeb().GetList("http://homepc/Lists/Sahil");

My proposed change: SPList SahilSurvey = properties.OpenWeb().Lists[properties.ListId];

You will notice that in my solution you dont have to hard code the list URL, and that way you make the event handler generic- able to handle all lists instead of just the one under "http://homepc/Lists/Sahil".


Anonymous said...


The proposed change to get the list is good. Anytime you can get away from hardcoding, that is a good thing.

The ListItem was null - I ran into the same issue. No I cannot use the ItemAdded event because I need to cancel the input, so I must use a synchronous event. This happens only in this kind of list however - if you set up a simple custom list, it seems to work.

Anyway, I did test my code, and it seemed to work.


Anonymous said...

I ran into this last week as well with a custom list. I talked to a PM on the WSS team and he said to access the property via indexing into the desired field. I thought I had already tried that, but when I tried properties.AfterProperties["Description"] where "Description" is my column's name, it worked. I looked in the debugger and saw that AfterProperties was still null with a count of 0, but indexing into it worked just fine. Here is some additional information they gave me:
In ItemUpdated you see the new value as the action has finished and that is the curent value.

So customer["Description"] will always give you the current value as it is the value that has been commited to the database.

In the ItemUpdating event handler the before properties is available only for document library type lists.

However, getting the property by indexing in the SPItemEventProperties collection should give you the new value that the field is being changed to.

So for example in your case if you went to the UI and changed the description from "Desc1" to"Desc2"

If you get the Item using the OM in the ItemUpdating event and do customer["Description"] you will see "Desc1"

However, If you do properties["Description"] you will see "Desc2"

Anonymous said...

Part II...the PM then sent me the following email (which I should've incorporated into my last response, sorry...):

Sorry, I meant the AfterProperties collection. I cut and Paste the wrong collection name.

In the ItemUpdating event

properties.AfterProperties["Description"] will give the new value

and customer["Description"] will give the existing database value.

In ItemUpdated you can not get this as the valued have been commited.

Anonymous said...

Another suggestion:
SPSite site = new SPSite(properties.WebUrl); //so that you don't need to hard-code the url
SPWeb web = site.OpenWeb();
SPList customers = web.Lists["Customers"];

...or even:

SPSite site = new SPSite(properties.WebUrl);
SPWeb web = site.OpenWeb();
SPList orders = web.Lists[properties.ListId]; // "Orders"

Anonymous said...

Is there anyway to get refernece to a form that has been saved in an itemAdding event.

Anonymous said...

Hi Ishai,

Did you find a way to get item data on ItemAdding event?


Anonymous said...

We are facing the same issue in which we want to access the AfterProperties of a list in ItemUpdating event but i am getting null values for it.Can you please help in this regard as its very urgent.The code i am using is:

string strTest = properties.AfterProperties["Column name"].ToString();

One of the comments in ur blog suggest that this thing works but it is not working.

Anonymous said...

Thanks for the info here it was really helpful.

I managed to also get information about the item in the ItemAdding event by using...


Note that the if the column display name has spaces in it (e.g. "Internal Column Name") then the internal column name will most likely be something like "Internal_x0020_Column_x0020_Name".


Anonymous said...

I just spent half a day trying to figure out why exactly the same thing with my event handler doesn't work when I override ItemAdded and now I see this post, will try suggested solution.

Thank you guys.

Anonymous said...

Another question, what is the best way to do it since it still seems that it doesn't work for me (I can't afford to hard code this)

Anonymous said...

Never mind, my mistake, it works fine