ItemDeleted - a would-be handy event receiver, but in reality no more useful than a box full of used matches.
An analogy, if I may, to explain the state of affairs
While away on business, you're phoned up by your home town police, and given the brief message:
- Someone very dear to you just passed away.
Caught slightly off guard, but immediately inquisitive, you might ask:
Which is rewarded with a mere:
- I don't know.
- What do you mean "I don't know"? For God's sake man, someone just died, and you can't tell me who?
- Well do you know anyone who could know, then?
- Great .. Thanks a heap.
What I'm saying is ...
ItemDeleted knows *nothing* about the item that was deleted, apart from its now invalid index into the list. While the event receiver properties does contain the fields BeforeProperties and AfterProperties, the former of which one would expect to contain some information about the state of affairs *prior* to the delete taking place; both are null.
Next up, there's ItemDeleting. After all, that signals the same possible state change, and unlike it's dimwitted brother 'Deleted, it *does* have the BeforeProperties initialized. Upon discovering this, you might decide to use this receiver to process item deletions, such as in my current project - to signal item changes across farm boundaries. What you're not considering, then, is that ItemDeleting is just an indication of what might happen. Any number of receivers following yours may set SPItemEventProperties.Cancel to abort the delete, thus possibly invalidating the state of your application (parts of it thinking an item is deleted, other parts knowing it's not).
So what can we do, then?
Parse the RecycleBin?
Possible, but even if we did iterate all items in there (possibly *a lot* of overhead), we don't have any idea at all what the items title, name or guid might have been.
Store event handler instance / static data in ItemDeleting, then check in ItemDeleted?
Unlike the RecycleBin, this would work, and it would give a reliable indication of what item was deleted, but it'd cost an arm and a leg efficiency-wise. Event handler instance data is a no-go, I should add, since event handler classes are initialized once per call to a receiver function (ugh). You could use an interlocked static field, but 1. it would be very inefficient in any environment with more than zero users (locking and unlocking the field for each call), 2. I would grant you *no* guarantees in a multi-frontend farm setup, or even a multi-worker process environment.
Store state info in the SPItemEventProperties.ReceiverData from ItemDeleting, then process it in ItemDeleted?
Once again: would require inefficient thread interlocking, possibly not visible across frontends / processes, and certainly the most ugly solution you'd ever put into production code. You should be ashamed.
Store data in [web properties / custom list / user profile / own database / an usb drive] from ItemDeleting, then process it in ItemDeleted?
Sure, that would work, and all apart from the usb drive it would probably be accessible from all processes / frontends. Your major pain in the neck here, though, is the lingering fact that ItemDeleted *may not be executed*. Imagine installing some third party WillProtectYourDataAndCrap wsp solution, which essentially will cancel all ItemDeleting events. Then imagine your custom table / properties / whatever, suddenly containing 50.000 could-once-have-been deleted entries, which were never processed or removed since ItemDeleted never executed. At that point: welcome a custom SPJobDefinition to clean up every x minutes. Also give yourself complementary slap on the back, since you've just implemented the worlds most over-engineered and likely-to-break-down-horribly "What was just deleted?"-mechanism.
Man, wouldn't it have been great if ItemDeleted could just tell you what was just deleted?