SharePoint 2010: Taxonomy issue and Event Receivers


February 2, 2012 - 11:59, by Steven Van de Craen - 0 Comments

Issue

Today I was troubleshooting a customer farm where Managed Metadata would remain empty in SharePoint, even though it was filled in correctly in the document’s Document Information Panel.

Digging through the internal XML structures of the DOCX and also the Content Type Schema and Field XML, I couldn’t find a reasonable explanation.

The library was configured with multiple Content Types, but the issue occurred only on some of them. It appeared that for those Content Types, the Managed Metadata field was Optional, not Required.

I tried reproducing that configuration in a new document library but there everything kept working, so the issue had to be with the existing library.

Further analysis, comparison and reflection showed that the problematic library was missing some Taxonomy-related Event Receivers.

List with Taxonomy Event Receivers

There appear to be four (4) Taxonomy Event Receivers:

  • TaxonomyItemSynchronousAddedEventReceiver (ItemAdding) and TaxonomyItemUpdatingEventReceiver (ItemUpdating) are added when a Managed Metadata field is added to the List

 

  • TaxonomyItemAddedAsyncEventReceiver (ItemAdded) and TaxonomyItemUpdatedEventReceiver (ItemUpdated) are added when “Metadata Publishing” is enabled in the List Settings

Metadata Publishing

 

The problematic library at the customer was lacking the first set of Event Receivers, which are responsible for syncing the hidden field.

ILSpy Taxonomy Event Receiver

Fix

I’ve written a one-off script (Console App) that loops all lists with a Managed Metadata field on all sites in the site collection and ensures the Taxonomy event receivers. That code was taken directly from TaxonomyField.AddEventReceiverIfNotExists.

using System; using System.IO; using System.Text; using System.Windows.Forms; using Microsoft.SharePoint; using Microsoft.SharePoint.Taxonomy; class Taxonomy_Fix { public static StringBuilder sb = new StringBuilder(); public static string nl = "\r\n"; public static string url = "http://intranet"; public static void FIXALL() { using (SPSite site = new SPSite(url)) { foreach (SPWeb web in site.AllWebs) { FixTaxonomyReceiverOnWebLists(web); web.Close(); } } File.AppendAllText(String.Format("c:\\{0:HH_mm_ss}.txt", DateTime.Now), sb.ToString()); MessageBox.Show(sb.ToString()); } private static void FixTaxonomyReceiverOnWebLists(SPWeb web) { for (int i = 0; i < web.Lists.Count; i++) { SPList list = web.Lists[i]; if (HasTaxonomyField(list)) { sb.AppendFormat("{0}{1} has Taxonomy Field{2}", url, list.RootFolder.ServerRelativeUrl, nl); EnsureTaxonomyHandlers(list); } } } private static bool HasTaxonomyField(SPList list) { bool result = false; foreach (SPField field in list.Fields) { if (field is TaxonomyField) { result = true; break; } } return result; } private static void EnsureTaxonomyHandlers(SPList list) { AddEventReceiverIfNotExists(list.EventReceivers, SPEventReceiverType.ItemAdding, typeof(TaxonomyField).Assembly.FullName, "Microsoft.SharePoint.Taxonomy.TaxonomyItemEventReceiver", "TaxonomyItemSynchronousAddedEventReceiver", SPEventReceiverSynchronization.Synchronous); AddEventReceiverIfNotExists(list.EventReceivers, SPEventReceiverType.ItemUpdating, typeof(TaxonomyField).Assembly.FullName, "Microsoft.SharePoint.Taxonomy.TaxonomyItemEventReceiver", "TaxonomyItemUpdatingEventReceiver", SPEventReceiverSynchronization.Synchronous); } private static void AddEventReceiverIfNotExists(SPEventReceiverDefinitionCollection eventReceiverCollection, SPEventReceiverType type, string assembly, string className, string receiverName, SPEventReceiverSynchronization sync) { if (eventReceiverCollection == null) { throw new ArgumentNullException("eventReceiverCollection"); } if (string.IsNullOrEmpty(assembly)) { throw new ArgumentNullException("Valid assembly name required"); } foreach (SPEventReceiverDefinition erd in eventReceiverCollection) { if (erd.Assembly == assembly && className == erd.Class && type == erd.Type && erd.Synchronization == sync) { sb.AppendFormat("\t>> no action required{0}", nl); return; } } SPEventReceiverDefinition erd2 = eventReceiverCollection.Add(); erd2.Name = receiverName; erd2.Type = type; erd2.Assembly = assembly; erd2.Class = className; erd2.Synchronization = sync; erd2.Update(); sb.AppendFormat("\t>> Added TaxonomyEvent Receiver{0}", nl); } }

Disclaimer: not responsible for this code. Run at own risk. Feel free to adapt or improve as desired or required.

Conclusion

I can’t really explain why only some Content Types were affected. I’m guessing the Optional/Required setting of the Managed Metadata field is involved somehow, but I didn’t really confirm that through testing.

Also not sure why the Event Receivers were missing in the first place. It could be because some “questionable” actions happened during the setup of the site, but it could as well be a bug in SharePoint 2010 RTM or later.


The sandbox is too busy to handle the request


December 7, 2011 - 16:32, by Steven Van de Craen - 2 Comments

SharePoint 2010 and SharePoint Online (Office 365) allow for custom development in the form of Sandbox Solutions. Your code is restricted to a subset of the SharePoint API but allows you do most regular operations inside a Site Collection.

Problem

Here’s a scenario that fails every time and everywhere:

  • Create a sandboxed Event Receiver that is registered to ItemUpdating
  • Create a sandboxed Web Part that does an SPListItem.Update() from the SharePoint Object Model
  • Watch how the sandbox errors out with the following message

[SPUserCodeSolutionExecutionFailedException: Unhandled exception was thrown by the sandboxed code wrapper's Execute method in the partial trust app domain: The sandboxed code execution request was refused because the Sandboxed Code Host Service was too busy to handle the request.]

  • Now just edit the item from the SharePoint UI and watch how that works wonderfully well

Sandbox Request Architecture

So what’s going on here ?

Sandbox request architecture

In light of this you might conclude that there’s no possible communication between two sandbox code requests (the Web Part and the Event Receiver). As good an explanation as any, so feel free to chime in.

Other things

Here are some other things I stumbled upon while researching this issue:

  • ItemUpdated is not affected and works fine
  • You cannot make your “after” events Synchronous in a Sandbox as they won’t fire
  • (Sandbox) Event Receivers can only be registered declaratively in the Feature XML
  • The certificate checking issue (crl.microsoft.com) has the same error message, but is unrelated to this issue
  • Triggering the update from non-Sandbox code works fine

Workaround

So how about we conclude with a workaround ?

In some cases you could use the “after” Event Receiver rather than the “before” Event Receiver, but isn’t really a sound solution.

The best option would be to rewrite the Web Part to run its code on the client, either through Client OM (ECMAScript or Silverlight), or the SharePoint Web Services.

Client Application Models in SharePoint 2010

http://msdn.microsoft.com/en-us/library/ff798452.aspx


SharePoint 2010: User cannot be found after using stsadm migrateuser


September 7, 2010 - 20:18, by Steven Van de Craen - 0 Comments

I’m wrapping up a SharePoint 2007 to 2010 migration with custom development including programmatically copying files and folders, custom Event Receivers, Web Parts, etc.

Since the new setup uses a new AD domain, user accounts were mapped to new accounts and migrated using stsadm –o migrateuser.

After this we noticed errors like “User cannot be found”.

A bit of investigation showed that the ‘migrateuser’ operation didn’t update SPFile.Author or SPFile.ModifiedBy and querying those properties would throw the above exception.

A look at the ItemXml confirmed this:

<z:row xmlns:z="#RowsetSchema" ows_FileLeafRef="3;#sample.pdf" ows_Modified_x0020_By="OLDDOMAIN\olduser" ows_Created_x0020_By="OLDDOMAIN\olduser" ows_File_x0020_Type="pdf" ows_ID="3" ows_Created="2010-06-28 15:20:48" ows_Author="133;#New User" ows_Modified="2010-09-07 18:41:07" ows_Editor="1073741823;#System Account" ows_File_x0020_Size="3;#1035567" ... />

Workaround

I didn’t want to script an update to all files (because that would trigger Event Receivers) so I avoided querying those properties directly and instead created an extension method to get those values through the SPListItem:

SPFieldUserValue fuv = new SPFieldUserValue(item.Web, (String)item["Author"]);
return fuv.User;

Scripting an update

There were a lot of Event Receivers in the custom solution so I didn’t take this path, mainly because I noticed in SharePoint 2010 the SPListItem.SystemUpdate() method will also trigger Event Receivers. Here’s a blog post on it:

http://blogs.msdn.com/b/mjsabby/archive/2010/01/24/disabling-events-in-sharepoint-2007-and-sharepoint-2010.aspx 

This is different from SharePoint 2007 as far as I recall. Feel free to comment on this.

Other mentions

This blog post by Keith Richie mentions a solution for SPWeb.Author: http://blog.krichie.com/2008/09/12/resetting-the-author-on-a-sharepoint-site-or-wherefore-art-thou-author-redux/

 

Be sure to take this into consideration !


Asynchronous Event Receivers and HttpContext


August 24, 2010 - 20:49, by Steven Van de Craen - 5 Comments

A lesser known trick to make use of the HttpContext in asynchronous Event Receivers in SharePoint 2007 or SharePoint 2010 is to define a constructor on the Event Receiver class that assigns the HttpContext to an internal or private member variable. Next you can access it from the method overrides for handling events.

public class MyER1 : SPItemEventReceiver
{
    // Local reference to HttpContext during Type Construction so we can use it in the Async Methods
    internal HttpContext _ctx = null;

    public MyER1()
    {
        _ctx = HttpContext.Current;
    }

    public override void ItemAdded(SPItemEventProperties properties)
    {
        string requestUrl = _ctx.Request.Url.ToString();
    }
}

There’s a difference in behavior for Lists and Libraries; the former allows you to also read/write from the Context.Session, while the latter doesn’t allow this (Session is null).

I’ve (ab)used this once on a project where I’d call a custom LayoutPage called with QueryString parameters to force a SPListItem.Update(). In the Event Receiver the code would interact with the values of those parameters.

Side note:

Doubt that there’s official support on this. Feel free to drop a comment with your opinion.


Event Receiver Definition: Data and Filter


June 30, 2009 - 11:05, by Steven Van de Craen - 0 Comments

I recently declared an Event Receiver through a SharePoint Feature: Event Registrations

Some of the samples online had surprising elements such as <Data /> and <Filter />. I have never seen them used before and didn’t know their purpose. Perhaps they are not to be messed with ? Perhaps great power lies within ?

  • Data – Specifies a string that will be passed as a parameter to the receiver method when handling the event

This one is commonly found in the documentation. It can be used to declare additional information and then the Event Receiver code behind can use this information. Better than hardcoding although I haven’t had the need for it (yet ?).

  • Filter – Reserved. Filter MUST be NULL

According to the official docs it is reserved: Event Receivers Result Set

 

 

Thank you Thomas for helping me on this one !

Also here’s a good reference: SharePoint 2007 Deployment: Event Registration Features


Event Receivers on Content Types


May 7, 2009 - 17:06, by Steven Van de Craen - 3 Comments

I'm currently doing quite some research on Event Receivers (ERs) on Content Types (CTs) and activating those CTs on a document library in SharePoint 2007 (WSS 3.0 to be exact, but same applies for MOSS 2007 and MSS(X) 2008).

The setup is a single document library with

  • the out of the box Document Content Type (no event receivers defined)
  • a custom ContentType1 having an EventReceiver1 for any possible event (ItemAdding, ItemUpdating, ItemAdded, …)
  • a custom ContentType2 having an EventReceiver2 for any possible event

In the scenario’s situation ‘A’ has Document as default Content Type, while situation ‘B’ has ContentType1 as default.

Some scenarios

1. Create a new document of type ‘Document’ (New button)

A. No events are fired because there are none defined for this Content Type
None

B. (same as A)

2. Create a new document of type ‘ContentType1’ (New button)

A. The Office client application “knows” the Content Type and will save directly as that and fire the defined events
EventReceiver1.ItemAdding, EventReceiver1.ItemAdded

B. (same as A)

3. Create a new document and save it to the document library (starting from Word)

A. The Office client application will ask the Content Type before saving and fire the events defined for that Content Type
None

B. (same as A)

4. Upload a document using the Single File page

A. All ItemAdding events fire. The default CT is assumed. Afterwards the user is presented with the option to change the Content Type which will trigger ItemUpdating and ItemUpdated for the destination Content Type
EventReceiver1.ItemAdding, EventReceiver2.ItemAdding

B. All ItemAdding events fire. The default CT is assumed. Other events defined for that Content Type also trigger
EventReceiver1.ItemAdding, EventReceiver2.ItemAdding, EventReceiver1.ItemAdded

5. Upload a document using the Multiple Files page

A. All ItemAdding events fire. The default CT is assumed
EventReceiver1.ItemAdding, EventReceiver2.ItemAdding

B. All ItemAdding events fire. The default CT is assumed
EventReceiver1.ItemAdding, EventReceiver2.ItemAdding, EventReceiver1.ItemAdded

6. Copy and paste a document of type ‘Document’

A. All ItemAdding events fire. The Content Type of the source file is assumed
EventReceiver1.ItemAdding, EventReceiver2.ItemAdding

B. (same as A)

7. Copy and paste a document of type ‘ContentType1’

A. All ItemAdding events fire. The Content Type of the source file is assumed. Other events defined for that Content Type also trigger (only Update, not Add)
EventReceiver1.ItemAdding, EventReceiver2.ItemAdding, EventReceiver1.ItemUpdating, EventReceiver1.ItemUpdated

B. (same as A)

8. Restore a document of type ‘Document’ from the Recycle Bin

A. All ItemAdding and ItemAdded events fire
EventReceiver1.ItemAdding, EventReceiver2.ItemAdding, EventReceiver1.Added, EventReceiver2.ItemAdded

B. (same as A)

9. Restore a document of type ‘ContentType1’ from the Recycle Bin

A. All ItemAdding and ItemAdded events fire
EventReceiver1.ItemAdding, EventReceiver2.ItemAdding, EventReceiver1.Added, EventReceiver2.ItemAdded

B. (same as A)

10. Restore a previous version of a document of type ‘Document’

A. No events fire
None

B. (same as A)

11. Restore a previous version of a document of type ‘ContentType1’

A. The ItemUpdating and ItemUpdated events defined for the Content Type fire
EventReceiver1.ItemUpdating, EventReceiver1.Updated

B. (same as A)

Lessons learned
  • All ItemAdding events trigger in case of file upload via WebDAV or the Upload page
  • Copying a file triggers the ItemAdding and ItemUpdating events, but not the ItemAdded event
  • Restoring from the Recycle Bin triggers all ItemAdding and ItemAdded events regardless of Content Type
  • Restoring a previous version is seen as an update (makes sense)

I’m starting to see the light although I do think that the Recycle Bin thing is a design flaw. Be careful on how you implement Event Receivers. Currently I’m thinking an additional check on Content Type in your code might be the safest way to ensure your code doesn’t run accidentally for a different Content Type ? What do you think ?


SharePoint 2007 Event Receiver and Enterprise Library: TargetInvocationException


December 15, 2008 - 16:08, by Steven Van de Craen - 0 Comments

Today I got into some code reviewing of an Item Event Receiver using Enterprise Library for data access. The problem occurred when registering the Event Receiver for a SharePoint List using the object model (SPList.EventReceivers.Add)

Exception has been thrown by the target of an invocation.

Here's a simplified view into the code:

public class MyEventReceiver: SPItemEventReceiver
{
     MyDataAccess da = new MyDataAccess();

     public override void ItemAdded(SPItemEventProperties properties)
     {
         ...
     }
}

public class MyDataAccess
{
     public MyDataAccess()
     {
          Microsoft.Practices.EnterpriseLibrary.Data.DatabaseFactory.CreateDatabase("MyDB");
     }

     ...
}

Apparently when the Event Receiver is registered it instantiates the MyDataAccess class which in turn calls the EntLib method in its constructor. This is what causes the exception.

A solution is to instantiate the MyDataAccess class in the methods rather than on event receiver initialisation:

public class MyEventReceiver: SPItemEventReceiver
{

     public override void ItemAdded(SPItemEventProperties properties)
     {
          MyDataAccess da = new MyDataAccess();
         ...
     }
}