Implementing Metadata Inheritance using an ItemEventReceiver
Posted
Monday, May 19, 2008 1:59 PM
by
CoreyRoth
When implementing an ECM solution, it is often necessary to come up with a way for documents to inherit metadata from a parent folder. The main reason of doing this is so that user's can search on any of the document's properties. Other ECM solutions do this for you (yes there are other ECM solutions other than SharePoint), but WSS requires a bit of code to make it happen. The way you do it is by creating an ItemEventReceiver and then by setting properties in the event handling method of various events. To start out, I am assuming you have created your own content types for a custom folder and document. We'll call these Custom Folder Type and Custom Document Type respectively. I am also assuming you are deploying these via feature. The reason is you need to set up an Item Event Receiver on the Custom Document Type and it is much easier to configure via feature.
The first thing we need to do is create the class to contain the event handling methods of the ItemAdded, ItemUpdated, and ItemCheckedIn events (there are more event types, but these are the only ones we need). I recommend putting this class in its own library (or in a library with other item event handling methods). My reason for this is that I have only been able to get an item event handling method to fire is when the assembly is in the GAC. If you have read my blog in the past, you know I almost always recommend against this, but in this case, I don't know of any choice. To implement the class, start by having your custom class inherit from SPItemEventReceiver. You can then override various event handling methods. In this case, I want to enforce the metadata inheritance whenever a document is added, updated, or checked in. To do this, we override the ItemAdded, ItemUpdated, and ItemCheckedIn methods. The contents of all the methods is typically the same. They call a method to do the inheritance and if it fails, we attempt to cancel the event (although I have never had it cancel anything when there is an exception).
public override void ItemAdded(SPItemEventProperties properties)
{
base.ItemAdded(properties);
try
{
InheritCustomProperties(properties.ListItem);
}
catch (Exception e)
{
properties.Cancel = true;
properties.ErrorMessage = "Error inheriting metadata.";
properties.Status = SPEventReceiverStatus.CancelWithError;
}
}
The SPItemEventProperties allows you to get access to the list item using the ListItem property. We then pass it to our custom method which does the inheritance. In this example, I am assuming that the parent folder has two properties: Product Id and Color, that we want to copy into the newly added (or updated) item.
protected void InheritCustomProperties(SPListItem childItem)
{
// make sure that the parentFolder and its item are not null
if ((childItem.File.ParentFolder != null) && (childItem.File.ParentFolder.Item != null))
{
SPItem parentItem = childItem.File.ParentFolder.Item;
// copy properties from parent to child
childItem["Product Id"] = parentItem["Product Id"];
childItem["Color"] = parentItem["Color"];
// event firing must be disabled otherwise this update will cause another event to fire
DisableEventFiring();
childItem.SystemUpdate(false);
EnableEventFiring();
}
}
One thing to make sure is that a parent item exists for you to inherit from (obviously you would need to do something different if you were uploading documents to the root of a library). Next it is just a matter of getting a reference to the parent item and then start copying each property. Before updating the child item, you need to make a call to the base class's DisableEventFiring method to keep it from firing an event when you save the item. Next, you should call SystemUpdate instead of Update which will save the item but not save it as a new version.
That is all the code that is involved. However, you still need to modify the XML of your Content Type feature to tell it to use your even receiver. In your content type's Elements.xml file, you'll need to add some new entries to the XmlDocuments element (add one if you don't have one already). Then, add a section similar to the one below to your file. You will see one receiver for each event type (ItemAdded, ItemUpdated, ItemCheckedIn).
<XmlDocument NamespaceURI="http://schemas.microsoft.com/sharepoint/events">
<spe:Receivers xmlns:spe="http://schemas.microsoft.com/sharepoint/events">
<Receiver>
<Name>Custom Document Added Event Handler</Name>
<Type>ItemAdded</Type>
<SequenceNumber>10001</SequenceNumber>
<Assembly>CustomEventReceivers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=f2d79125eb887b9e</Assembly>
<Class>CustomEventReceivers.CustomDocumentLibraryItemEventReceiver</Class>
<Data />
<Filter />
</Receiver>
<Receiver>
<Name>Custom Document Updated Event Handler</Name>
<Type>ItemUpdated</Type>
<SequenceNumber>10001</SequenceNumber>
<Assembly>CustomEventReceivers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=f2d79125eb887b9e</Assembly>
<Class>CustomEventReceivers.CustomDocumentLibraryItemEventReceiver</Class>
<Data />
<Filter />
</Receiver>
<Receiver>
<Name>Custom Document Check In Event Handler</Name>
<Type>ItemCheckedIn</Type>
<SequenceNumber>10001</SequenceNumber>
<Assembly>CustomEventReceivers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=f2d79125eb887b9e</Assembly>
<Class>CustomEventReceivers.CustomDocumentLibraryItemEventReceiver</Class>
<Data />
<Filter />
</Receiver>
</spe:Receivers>
</XmlDocument>
The SequenceNumber is the order in which your event handling method fires. Microsoft typically recommends something starting with 10,000 and above to avoid conflicts with their event handlers. The Assembly element, takes a standard assembly reference to a DLL in the GAC. The Class element specifies the namespace and class which contains the event handling methods.
Again, I recommend keeping the feature for your content type and your event receivers in different assemblies. Install your assemblies first with a solution package. Then you can install the feature containing your updated content type. Once you have it installed, add some metadata to a parent item, save it, and then try uploading a new document to verify that the metadata was copied to the new document. If the event handler threw an exception, most likely it did not display a visible error. Unfortunately, this means you'll have to do some GAC debugging. It sounds like quite a bit of work, but it really isn't bad.